@stableflow/wallet-evm 0.0.0-experimental-5a8afd7389-20260703

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/dist/index.js ADDED
@@ -0,0 +1,1557 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@stableflow/core');
6
+ var bridges = require('@stableflow/bridges');
7
+ var utilsEvm = require('@stableflow/utils-evm');
8
+ var utilsSolana = require('@stableflow/utils-solana');
9
+ var Big = require('big.js');
10
+ var ethers = require('ethers');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var Big__default = /*#__PURE__*/_interopDefault(Big);
15
+
16
+ // src/evm.ts
17
+ var DEFAULT_GAS_LIMIT = 100000n;
18
+ var DEFAULT_GAS_LIMIT_FAILED = 4000000n;
19
+ var EVMWallet = class {
20
+ constructor(_provider, _signer) {
21
+ this.provider = _provider;
22
+ this.signer = _signer;
23
+ const cs = new core.Csl(core.OpenAPI.DEBUG);
24
+ this.csl = cs.log;
25
+ }
26
+ async transfer(data) {
27
+ const { originAsset, depositAddress, amount } = data;
28
+ if (originAsset === "eth") {
29
+ const tx2 = await this.signer.sendTransaction({
30
+ to: depositAddress,
31
+ value: ethers.ethers.parseEther(amount)
32
+ });
33
+ await tx2.wait();
34
+ return tx2;
35
+ }
36
+ const contract = new ethers.ethers.Contract(originAsset, core.erc20Abi, this.signer);
37
+ const tx = await contract.transfer(depositAddress, amount);
38
+ const result = await tx.wait();
39
+ return result.hash;
40
+ }
41
+ async getBalance(token, account, options) {
42
+ const { isCatchError = false } = options || {};
43
+ try {
44
+ let provider = this.provider;
45
+ if (token.rpcUrls) {
46
+ provider = utilsEvm.evmRpcFallbackProvider(token);
47
+ }
48
+ if (token.symbol === "eth" || token.symbol === "ETH" || token.symbol === "native") {
49
+ const balance2 = await provider.getBalance(account);
50
+ return balance2.toString();
51
+ }
52
+ const contract = new ethers.ethers.Contract(token.contractAddress, core.erc20Abi, provider);
53
+ const balance = await contract.balanceOf(account);
54
+ this.csl("EVM getBalance", "green-500", "Success getting %s token balance: %o", token.contractAddress, balance);
55
+ return balance.toString();
56
+ } catch (err) {
57
+ this.csl("EVM getBalance", "red-500", "Get balance failed: %o", err);
58
+ if (isCatchError) {
59
+ throw err;
60
+ }
61
+ return "0";
62
+ }
63
+ }
64
+ async balanceOf(token, account, options) {
65
+ return await this.getBalance(token, account, options);
66
+ }
67
+ /**
68
+ * Estimate gas limit for transfer transaction
69
+ * @param data Transfer data
70
+ * @returns Gas limit estimate, gas price, and estimated gas cost
71
+ */
72
+ async estimateTransferGas(data) {
73
+ const { fromToken, depositAddress, amount, account } = data;
74
+ const originAsset = fromToken.contractAddress;
75
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
76
+ let gasLimit;
77
+ if (originAsset === "eth") {
78
+ const tx = {
79
+ from: account,
80
+ to: depositAddress,
81
+ value: ethers.ethers.parseEther(amount)
82
+ };
83
+ gasLimit = await provider.estimateGas(tx);
84
+ } else {
85
+ const contract = new ethers.ethers.Contract(originAsset, core.erc20Abi, provider);
86
+ gasLimit = await contract.transfer.estimateGas(depositAddress, amount);
87
+ }
88
+ gasLimit = gasLimit * 120n / 100n;
89
+ const feeData = await provider.getFeeData();
90
+ const gasPrice = feeData.gasPrice || 0n;
91
+ const estimateGas = gasLimit * gasPrice;
92
+ return {
93
+ gasLimit,
94
+ gasPrice,
95
+ estimateGas
96
+ };
97
+ }
98
+ getContract(params) {
99
+ const {
100
+ contractAddress,
101
+ abi
102
+ } = params;
103
+ return new ethers.ethers.Contract(contractAddress, abi, this.signer);
104
+ }
105
+ async allowance(params) {
106
+ const {
107
+ dry,
108
+ contractAddress,
109
+ spender,
110
+ address,
111
+ amountWei,
112
+ provider,
113
+ blockTag,
114
+ strict = false
115
+ } = params;
116
+ const runner = provider || this.provider;
117
+ const contract = new ethers.ethers.Contract(contractAddress, core.erc20Abi, runner);
118
+ if (dry) {
119
+ return {
120
+ contract,
121
+ allowance: "0",
122
+ needApprove: false
123
+ };
124
+ }
125
+ let allowance = "0";
126
+ try {
127
+ allowance = blockTag ? await contract.allowance(address, spender, { blockTag }) : await contract.allowance(address, spender);
128
+ allowance = allowance.toString();
129
+ } catch (error) {
130
+ this.csl("EVM allowance", "red-500", "Error getting allowance: %o", error);
131
+ if (strict) {
132
+ throw error;
133
+ }
134
+ }
135
+ return {
136
+ contract,
137
+ allowance,
138
+ needApprove: Big__default.default(amountWei || 0).gt(allowance || 0)
139
+ };
140
+ }
141
+ async approve(params) {
142
+ const {
143
+ contractAddress,
144
+ spender,
145
+ amountWei,
146
+ isApproveMax = false,
147
+ isDetails = false,
148
+ isWaitTxReceipt = true
149
+ } = params;
150
+ const contract = new ethers.ethers.Contract(contractAddress, core.erc20Abi, this.signer);
151
+ let _amountWei = amountWei;
152
+ if (isApproveMax) {
153
+ _amountWei = ethers.ethers.MaxUint256;
154
+ }
155
+ const detailResult = {
156
+ success: false,
157
+ data: {},
158
+ message: null
159
+ };
160
+ try {
161
+ const tx = await contract.approve(spender, _amountWei);
162
+ const txReceipt = await tx.wait();
163
+ if (txReceipt.status === 1) {
164
+ if (isDetails) {
165
+ const confirmations = typeof txReceipt.confirmations === "function" ? await txReceipt.confirmations() : txReceipt.confirmations;
166
+ detailResult.success = true;
167
+ detailResult.data = {
168
+ txHash: txReceipt.hash,
169
+ blockNumber: txReceipt.blockNumber,
170
+ confirmations
171
+ };
172
+ return detailResult;
173
+ }
174
+ return true;
175
+ }
176
+ if (isDetails) {
177
+ detailResult.message = "Arrove failed";
178
+ return detailResult;
179
+ }
180
+ return false;
181
+ } catch (error) {
182
+ this.csl("EVM approve", "red-500", "Error approve: %o", error);
183
+ if (isDetails) {
184
+ detailResult.message = error.message;
185
+ }
186
+ }
187
+ if (isDetails) {
188
+ return detailResult;
189
+ }
190
+ return false;
191
+ }
192
+ async getEstimateGas(params) {
193
+ const { gasLimit, price, nativeToken, provider, gasPrice } = params;
194
+ let finalGasPrice = gasPrice;
195
+ if (!finalGasPrice) {
196
+ const feeData = await provider.getFeeData();
197
+ finalGasPrice = feeData.maxFeePerGas || feeData.gasPrice || BigInt("20000000000");
198
+ }
199
+ const estimateGas = BigInt(gasLimit) * BigInt(finalGasPrice);
200
+ const estimateGasAmount = Big__default.default(estimateGas.toString()).div(10 ** nativeToken.decimals);
201
+ const estimateGasUsd = Big__default.default(estimateGasAmount).times(price || 1);
202
+ return {
203
+ gasPrice: finalGasPrice,
204
+ usd: core.numberRemoveEndZero(Big__default.default(estimateGasUsd).toFixed(20)),
205
+ wei: estimateGas,
206
+ amount: core.numberRemoveEndZero(Big__default.default(estimateGasAmount).toFixed(nativeToken.decimals))
207
+ };
208
+ }
209
+ async estimateTransaction(params) {
210
+ const {
211
+ dry,
212
+ contract,
213
+ method,
214
+ param,
215
+ fromToken,
216
+ prices,
217
+ evmGasFees,
218
+ defaultGasLimit = DEFAULT_GAS_LIMIT,
219
+ refundTo,
220
+ txRequest
221
+ } = params;
222
+ const cs = new core.Csl(core.OpenAPI.DEBUG);
223
+ const csl = cs.log;
224
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
225
+ const nativeTokenPrice = core.getPrice(prices, fromToken.nativeToken.symbol);
226
+ const result = {
227
+ estimateSourceGasLimit: dry ? DEFAULT_GAS_LIMIT_FAILED : DEFAULT_GAS_LIMIT,
228
+ estimateSourceGas: 0n,
229
+ estimateSourceGasUsd: "0"
230
+ };
231
+ const setDefaultGasLimit = async () => {
232
+ const { usd, wei } = await this.getEstimateGas({
233
+ gasLimit: DEFAULT_GAS_LIMIT,
234
+ price: nativeTokenPrice,
235
+ nativeToken: fromToken.nativeToken,
236
+ provider,
237
+ gasPrice: dry ? evmGasFees[fromToken.chainId].gasPrice : void 0
238
+ });
239
+ result.estimateSourceGas = wei;
240
+ result.estimateSourceGasUsd = usd;
241
+ };
242
+ let finalGasLimit = defaultGasLimit;
243
+ if (dry) {
244
+ await setDefaultGasLimit();
245
+ return result;
246
+ }
247
+ if (txRequest) {
248
+ try {
249
+ const gasLimit = await provider.estimateGas({
250
+ to: txRequest.target,
251
+ data: txRequest.calldata,
252
+ from: refundTo || this.signer?.address
253
+ });
254
+ finalGasLimit = gasLimit * 120n / 100n;
255
+ const { usd, wei } = await this.getEstimateGas({
256
+ gasLimit: finalGasLimit,
257
+ price: nativeTokenPrice,
258
+ nativeToken: fromToken.nativeToken,
259
+ provider
260
+ });
261
+ result.estimateSourceGasLimit = finalGasLimit;
262
+ result.estimateSourceGas = wei;
263
+ result.estimateSourceGasUsd = usd;
264
+ } catch (error) {
265
+ csl("EVM estimateTransaction", "red-500", "%s estimateGas failed: %o", method, error);
266
+ await setDefaultGasLimit();
267
+ result.estimateSourceGasLimit = DEFAULT_GAS_LIMIT_FAILED / 2n;
268
+ }
269
+ return result;
270
+ }
271
+ try {
272
+ const gasLimit = await contract[method].estimateGas(...param);
273
+ finalGasLimit = gasLimit * 120n / 100n;
274
+ const { usd, wei } = await this.getEstimateGas({
275
+ gasLimit: finalGasLimit,
276
+ price: nativeTokenPrice,
277
+ nativeToken: fromToken.nativeToken,
278
+ provider
279
+ });
280
+ result.estimateSourceGasLimit = finalGasLimit;
281
+ result.estimateSourceGas = wei;
282
+ result.estimateSourceGasUsd = usd;
283
+ } catch (error) {
284
+ csl("EVM estimateTransaction", "red-500", "%s estimateGas failed: %o", method, error);
285
+ await setDefaultGasLimit();
286
+ result.estimateSourceGasLimit = DEFAULT_GAS_LIMIT_FAILED / 2n;
287
+ }
288
+ return result;
289
+ }
290
+ async quoteOFT(params) {
291
+ const { Options } = await import('@layerzerolabs/lz-v2-utilities');
292
+ const {
293
+ dry,
294
+ abi,
295
+ dstEid,
296
+ recipient,
297
+ amountWei,
298
+ slippageTolerance,
299
+ payInLzToken,
300
+ fromToken,
301
+ toToken,
302
+ prices,
303
+ evmGasFees,
304
+ originLayerzeroAddress,
305
+ destinationLayerzeroAddress,
306
+ excludeFees,
307
+ refundTo,
308
+ multiHopComposer,
309
+ isMultiHopComposer,
310
+ isOriginLegacy,
311
+ isDestinationLegacy,
312
+ originLayerzero,
313
+ destinationLayerzero
314
+ } = params;
315
+ const result = {
316
+ needApprove: false,
317
+ approveSpender: originLayerzeroAddress,
318
+ sendParam: void 0,
319
+ quoteParam: {
320
+ ...params,
321
+ originLayerzeroAddress,
322
+ destinationLayerzeroAddress
323
+ },
324
+ fees: {},
325
+ totalFeesUsd: "0",
326
+ estimateSourceGas: 0n,
327
+ totalEstimateSourceGas: 0n,
328
+ estimateSourceGasUsd: "0",
329
+ estimateTime: 0,
330
+ // seconds - dynamically calculated using LayerZero formula
331
+ outputAmount: core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** params.fromToken.decimals).toFixed(params.fromToken.decimals, 0))
332
+ };
333
+ const execTime = new core.ExecTime({ type: `Usdt0 EVM ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-100", isDebug: core.OpenAPI.DEBUG });
334
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
335
+ const oftContract = new ethers.ethers.Contract(originLayerzeroAddress, abi, this.signer);
336
+ const oftContractRead = new ethers.ethers.Contract(originLayerzeroAddress, abi, provider);
337
+ execTime.breakpoint();
338
+ const approvalRequired = isOriginLegacy ? originLayerzero.oftLegacyApprovalRequired : originLayerzero.oftApprovalRequired;
339
+ this.csl("EVM quoteOFT", "blue-900", "approvalRequired: %o", approvalRequired);
340
+ execTime.log("approvalRequired");
341
+ const lzReceiveOptionGas = isDestinationLegacy ? destinationLayerzero.lzReceiveOptionGasLegacy : destinationLayerzero.lzReceiveOptionGas || 2e5;
342
+ let lzReceiveOptionValue = 0;
343
+ execTime.breakpoint();
344
+ const destATA = await utilsSolana.getDestinationAssociatedTokenAddress({
345
+ recipient,
346
+ toToken
347
+ });
348
+ execTime.log("getDestinationAssociatedTokenAddress");
349
+ if (destATA.needCreateTokenAccount) {
350
+ lzReceiveOptionValue = bridges.LZ_RECEIVE_VALUE[toToken.chainName] || 0;
351
+ }
352
+ let unMultiHopExtraOptions = Options.newOptions().toHex();
353
+ if (!isMultiHopComposer && lzReceiveOptionValue) {
354
+ unMultiHopExtraOptions = Options.newOptions().addExecutorLzReceiveOption(lzReceiveOptionGas, lzReceiveOptionValue).toHex();
355
+ }
356
+ const sendParam = {
357
+ dstEid,
358
+ to: bridges.addressToBytes32(toToken.chainType, recipient),
359
+ amountLD: amountWei,
360
+ minAmountLD: 0n,
361
+ extraOptions: unMultiHopExtraOptions,
362
+ composeMsg: "0x",
363
+ oftCmd: "0x"
364
+ };
365
+ if (isMultiHopComposer) {
366
+ sendParam.dstEid = multiHopComposer.eid;
367
+ sendParam.to = bridges.addressToBytes32("evm", multiHopComposer.oftMultiHopComposer);
368
+ let multiHopExtraOptions = Options.newOptions().toHex();
369
+ if (lzReceiveOptionValue) {
370
+ multiHopExtraOptions = Options.newOptions().addExecutorLzReceiveOption(lzReceiveOptionGas, lzReceiveOptionValue).toHex();
371
+ }
372
+ const composeMsgSendParam = {
373
+ dstEid,
374
+ to: bridges.addressToBytes32(toToken.chainType, recipient),
375
+ amountLD: sendParam.amountLD,
376
+ minAmountLD: sendParam.minAmountLD,
377
+ extraOptions: multiHopExtraOptions,
378
+ composeMsg: "0x",
379
+ oftCmd: "0x"
380
+ };
381
+ execTime.breakpoint();
382
+ const hopMsgFee = await bridges.getHopMsgFee({
383
+ sendParam: composeMsgSendParam,
384
+ toToken
385
+ });
386
+ execTime.log("getHopMsgFee");
387
+ sendParam.extraOptions = Options.newOptions().addExecutorComposeOption(0, originLayerzero.composeOptionGas || 8e5, hopMsgFee).toHex();
388
+ const abiCoder = ethers.ethers.AbiCoder.defaultAbiCoder();
389
+ sendParam.composeMsg = abiCoder.encode(
390
+ ["tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd)"],
391
+ [Object.values(composeMsgSendParam)]
392
+ );
393
+ }
394
+ if (isOriginLegacy || isDestinationLegacy) {
395
+ result.fees.legacyMeshFeeUsd = core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** params.fromToken.decimals).times(bridges.USDT0_LEGACY_MESH_TRANSFTER_FEE).toFixed(params.fromToken.decimals));
396
+ result.outputAmount = core.numberRemoveEndZero(Big__default.default(Big__default.default(amountWei || 0).div(10 ** params.fromToken.decimals)).minus(result.fees.legacyMeshFeeUsd || 0).toFixed(params.fromToken.decimals, 0));
397
+ }
398
+ if (!dry) {
399
+ execTime.breakpoint();
400
+ const oftData = await oftContractRead.quoteOFT.staticCall(sendParam);
401
+ const [, , oftReceipt] = oftData;
402
+ sendParam.minAmountLD = Big__default.default(amountWei).times(Big__default.default(1).minus(Big__default.default(slippageTolerance || 0).div(100))).toFixed(0);
403
+ result.outputAmount = core.numberRemoveEndZero(core.numberRemoveEndZero(Big__default.default(oftReceipt[1].toString()).div(10 ** params.toToken.decimals).toFixed(params.toToken.decimals, 0)));
404
+ execTime.log("quoteOFT.staticCall");
405
+ }
406
+ this.csl("EVMWallet quoteOFT", "red-600", "result.outputAmount: %o", result.outputAmount);
407
+ this.csl("EVMWallet quoteOFT", "red-600", "slippageTolerance: %o", slippageTolerance + "%");
408
+ this.csl("EVMWallet quoteOFT", "red-600", "Minimum received amount: %o", Big__default.default(amountWei).div(10 ** fromToken.decimals).times(Big__default.default(1).minus(Big__default.default(slippageTolerance || 0).div(100))).toFixed(6, 0));
409
+ if (Big__default.default(result.outputAmount).lt(Big__default.default(amountWei).div(10 ** fromToken.decimals).times(Big__default.default(1).minus(Big__default.default(slippageTolerance || 0).div(100))))) {
410
+ result.errMsg = "Slippage limit exceeded";
411
+ return result;
412
+ }
413
+ execTime.breakpoint();
414
+ const mergedCall = [
415
+ oftContractRead.quoteSend.staticCall(sendParam, payInLzToken)
416
+ ];
417
+ if (approvalRequired) {
418
+ mergedCall.push(
419
+ this.allowance({
420
+ dry,
421
+ contractAddress: fromToken.contractAddress,
422
+ spender: originLayerzeroAddress,
423
+ address: refundTo,
424
+ amountWei,
425
+ provider
426
+ })
427
+ );
428
+ }
429
+ const [msgFee, allowanceResult] = await Promise.all(mergedCall);
430
+ if (approvalRequired) {
431
+ result.needApprove = allowanceResult?.needApprove;
432
+ }
433
+ execTime.log("quoteSend.staticCall and allowance");
434
+ let nativeMsgFee = msgFee[0];
435
+ const lzMsgFee = msgFee[1];
436
+ result.totalEstimateSourceGas = nativeMsgFee;
437
+ nativeMsgFee = nativeMsgFee * utilsEvm.NATIVE_MSG_FEE_BUFFER / 100n;
438
+ const nativeFeeUsd = Big__default.default(nativeMsgFee?.toString() || 0).div(10 ** fromToken.nativeToken.decimals).times(core.getPrice(prices, fromToken.nativeToken.symbol));
439
+ result.fees.nativeFee = core.numberRemoveEndZero(Big__default.default(nativeMsgFee?.toString() || 0).div(10 ** fromToken.nativeToken.decimals).toFixed(fromToken.nativeToken.decimals));
440
+ result.fees.nativeFeeUsd = core.numberRemoveEndZero(Big__default.default(nativeFeeUsd).toFixed(20));
441
+ result.fees.lzTokenFeeUsd = core.numberRemoveEndZero(Big__default.default(lzMsgFee?.toString() || 0).div(10 ** fromToken.decimals).toFixed(20));
442
+ const finalSendParam = [
443
+ sendParam,
444
+ {
445
+ nativeFee: nativeMsgFee,
446
+ lzTokenFee: lzMsgFee
447
+ },
448
+ refundTo,
449
+ { value: nativeMsgFee }
450
+ ];
451
+ execTime.breakpoint();
452
+ const ett = await this.estimateTransaction({
453
+ dry,
454
+ contract: oftContract,
455
+ method: "send",
456
+ param: finalSendParam,
457
+ fromToken,
458
+ prices,
459
+ evmGasFees
460
+ });
461
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
462
+ result.estimateSourceGas = ett.estimateSourceGas;
463
+ result.totalEstimateSourceGas += ett.estimateSourceGas;
464
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
465
+ finalSendParam[3].gasLimit = ett.estimateSourceGasLimit;
466
+ execTime.log("send.estimateGas");
467
+ result.sendParam = {
468
+ contract: oftContract,
469
+ method: "send",
470
+ param: finalSendParam
471
+ };
472
+ for (const feeKey in result.fees) {
473
+ if (excludeFees.includes(feeKey) || !/Usd$/.test(feeKey)) {
474
+ continue;
475
+ }
476
+ result.totalFeesUsd = Big__default.default(result.totalFeesUsd || 0).plus(result.fees[feeKey] || 0);
477
+ }
478
+ result.totalFeesUsd = core.numberRemoveEndZero(Big__default.default(result.totalFeesUsd).toFixed(20));
479
+ execTime.logTotal("quoteOFT");
480
+ return result;
481
+ }
482
+ async sendTransaction(params) {
483
+ const {
484
+ method,
485
+ contract,
486
+ param
487
+ } = params;
488
+ const overridesIndex = param.length - 1;
489
+ const overrides = param[overridesIndex] && typeof param[overridesIndex] === "object" && !Array.isArray(param[overridesIndex]) ? { ...param[overridesIndex] } : {};
490
+ if (!overrides.maxFeePerGas) {
491
+ try {
492
+ const feeData = await this.provider.getFeeData();
493
+ if (feeData.maxFeePerGas) {
494
+ overrides.maxFeePerGas = feeData.maxFeePerGas * 120n / 100n;
495
+ }
496
+ if (feeData.maxPriorityFeePerGas && !overrides.maxPriorityFeePerGas) {
497
+ overrides.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
498
+ }
499
+ } catch (error) {
500
+ this.csl("EVM sendTransaction", "red-500", "Failed to get fee data for gas buffer: %o", error);
501
+ }
502
+ }
503
+ const finalParam = [...param];
504
+ if (param[overridesIndex] && typeof param[overridesIndex] === "object" && !Array.isArray(param[overridesIndex])) {
505
+ finalParam[overridesIndex] = overrides;
506
+ } else {
507
+ finalParam.push(overrides);
508
+ }
509
+ try {
510
+ const tx = await contract[method](...finalParam);
511
+ return tx.hash;
512
+ } catch (error) {
513
+ this.csl("EVM sendTransaction", "red-500", "Error sending transaction: %o, message: %o", error, error.message);
514
+ let _finalErrorMessage = `Transaction failed: ${error.message}`;
515
+ if (error?.message?.includes("user rejected action")) {
516
+ _finalErrorMessage = error.message;
517
+ }
518
+ throw new Error(_finalErrorMessage);
519
+ }
520
+ }
521
+ /**
522
+ * Unified quote method that routes to specific quote methods based on type
523
+ * @param type Service type from Service
524
+ * @param params Parameters for the quote
525
+ */
526
+ async quote(type, params) {
527
+ switch (type) {
528
+ case core.Service.CCTP:
529
+ return await this.quoteCCTP(params);
530
+ case core.Service.Usdt0:
531
+ return await this.quoteOFT(params);
532
+ case core.Service.OneClick:
533
+ return await this.quoteOneClickProxy(params);
534
+ case core.Service.Native:
535
+ return await this.quoteNative(params);
536
+ case core.Service.FraxZero:
537
+ return await this.quoteFraxZero(params);
538
+ default:
539
+ throw new Error(`Unsupported quote type: ${type}`);
540
+ }
541
+ }
542
+ /**
543
+ * Unified send method that routes to specific send methods based on type
544
+ * @param type Send type from SendType enum
545
+ * @param params Parameters for the send transaction
546
+ */
547
+ async send(type, params) {
548
+ switch (type) {
549
+ case core.SendType.SEND:
550
+ return await this.sendTransaction(params);
551
+ case core.SendType.TRANSFER:
552
+ return await this.transfer(params);
553
+ default:
554
+ throw new Error(`Unsupported send type: ${type}`);
555
+ }
556
+ }
557
+ async quoteCCTP(params) {
558
+ const {
559
+ dry,
560
+ proxyAddress,
561
+ abi,
562
+ refundTo,
563
+ recipient,
564
+ amountWei,
565
+ // slippageTolerance,
566
+ fromToken,
567
+ toToken,
568
+ prices,
569
+ evmGasFees,
570
+ excludeFees,
571
+ destinationDomain,
572
+ sourceDomain
573
+ } = params;
574
+ const result = {
575
+ needApprove: false,
576
+ approveSpender: proxyAddress,
577
+ sendParam: void 0,
578
+ quoteParam: {
579
+ sourceDomain,
580
+ destinationDomain,
581
+ proxyAddress,
582
+ ...params
583
+ },
584
+ fees: {},
585
+ totalFeesUsd: void 0,
586
+ estimateSourceGas: void 0,
587
+ totalEstimateSourceGas: 0n,
588
+ estimateSourceGasUsd: void 0,
589
+ estimateTime: Math.floor(Math.random() * 8) + 3,
590
+ outputAmount: core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** fromToken.decimals).toFixed(fromToken.decimals, 0))
591
+ };
592
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
593
+ const execTime = new core.ExecTime({ type: `CCTP EVM ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-200", isDebug: core.OpenAPI.DEBUG });
594
+ const proxyContract = new ethers.ethers.Contract(proxyAddress, abi, this.signer);
595
+ new ethers.ethers.Contract(proxyAddress, abi, provider);
596
+ let realRecipient = recipient;
597
+ execTime.breakpoint();
598
+ const getUsrNonce = async () => {
599
+ let userNonce2 = 0n;
600
+ try {
601
+ userNonce2 = await proxyContract.userNonces(refundTo);
602
+ } catch (error) {
603
+ }
604
+ return userNonce2;
605
+ };
606
+ const mergedCalls = [
607
+ utilsSolana.getDestinationAssociatedTokenAddress({
608
+ recipient,
609
+ toToken
610
+ }),
611
+ getUsrNonce(),
612
+ this.allowance({
613
+ dry,
614
+ contractAddress: fromToken.contractAddress,
615
+ address: refundTo,
616
+ spender: proxyAddress,
617
+ amountWei,
618
+ provider
619
+ })
620
+ ];
621
+ const [destATA, userNonce, allowance] = await Promise.all(mergedCalls);
622
+ execTime.log("getDestinationATA & getUsrNonce & get allowance", "destATA: %o, userNonce: %o, allowance: %o", destATA, userNonce, allowance);
623
+ result.needCreateTokenAccount = destATA.needCreateTokenAccount;
624
+ if (destATA.associatedTokenAddress) {
625
+ realRecipient = destATA.associatedTokenAddress;
626
+ }
627
+ result.needApprove = allowance.needApprove;
628
+ execTime.breakpoint();
629
+ const signatureRes = await bridges.quoteSignature({
630
+ address: refundTo,
631
+ amount: core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** fromToken.decimals).toFixed(fromToken.decimals, 0)),
632
+ destination_domain_id: destinationDomain,
633
+ receipt_address: realRecipient,
634
+ source_domain_id: sourceDomain,
635
+ user_nonce: Number(userNonce)
636
+ });
637
+ const {
638
+ bridge_fee,
639
+ finality_threshold,
640
+ max_fee,
641
+ mint_fee,
642
+ receipt_amount,
643
+ signature,
644
+ destination_caller
645
+ } = signatureRes;
646
+ execTime.log("quoteSignature");
647
+ result.fees.estimateMintGasUsd = core.numberRemoveEndZero(Big__default.default(mint_fee || 0).div(10 ** fromToken.decimals).toFixed(fromToken.decimals));
648
+ result.fees.bridgeFeeUsd = core.numberRemoveEndZero(Big__default.default(bridge_fee || 0).div(10 ** fromToken.decimals).toFixed(fromToken.decimals));
649
+ const chargedAmount = BigInt(amountWei) - BigInt(mint_fee);
650
+ result.outputAmount = core.numberRemoveEndZero(Big__default.default(receipt_amount || 0).div(10 ** fromToken.decimals).toFixed(fromToken.decimals, 0));
651
+ const depositParam = [
652
+ // originalAmount
653
+ amountWei,
654
+ // chargedAmount = originalAmount - gas fee
655
+ chargedAmount,
656
+ // destinationDomain
657
+ destinationDomain,
658
+ // mintRecipient
659
+ bridges.addressToBytes32(toToken.chainType, realRecipient),
660
+ // burnToken
661
+ fromToken.contractAddress,
662
+ // destinationCaller
663
+ destination_caller ? bridges.addressToBytes32(toToken.chainType, destination_caller) : "0x0000000000000000000000000000000000000000000000000000000000000000",
664
+ // maxFee
665
+ max_fee,
666
+ // minFinalityThreshold
667
+ finality_threshold,
668
+ // signature
669
+ signature
670
+ ];
671
+ execTime.breakpoint();
672
+ const ett = await this.estimateTransaction({
673
+ dry,
674
+ contract: proxyContract,
675
+ method: "depositWithFee",
676
+ param: depositParam,
677
+ fromToken,
678
+ prices,
679
+ evmGasFees
680
+ });
681
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
682
+ result.estimateSourceGas = ett.estimateSourceGas;
683
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
684
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
685
+ depositParam.push({ gasLimit: ett.estimateSourceGasLimit });
686
+ execTime.log("estimateTransaction");
687
+ result.sendParam = {
688
+ method: "depositWithFee",
689
+ contract: proxyContract,
690
+ param: depositParam
691
+ };
692
+ for (const feeKey in result.fees) {
693
+ if (excludeFees.includes(feeKey) || !/Usd$/.test(feeKey)) {
694
+ continue;
695
+ }
696
+ result.totalFeesUsd = Big__default.default(result.totalFeesUsd || 0).plus(result.fees[feeKey] || 0);
697
+ }
698
+ result.totalFeesUsd = core.numberRemoveEndZero(Big__default.default(result.totalFeesUsd).toFixed(20));
699
+ execTime.logTotal("quoteCCTP");
700
+ return result;
701
+ }
702
+ async quoteOneClickProxy(params) {
703
+ const {
704
+ dry,
705
+ proxyAddress,
706
+ abi,
707
+ fromToken,
708
+ refundTo,
709
+ depositAddress,
710
+ amountWei,
711
+ prices,
712
+ evmGasFees
713
+ } = params;
714
+ const execTime = new core.ExecTime({ type: `OneClick EVM ${fromToken.chainName}`, logStyle: "stone-300", isDebug: core.OpenAPI.DEBUG });
715
+ const result = { fees: {} };
716
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
717
+ const proxyContract = new ethers.ethers.Contract(proxyAddress, abi, this.signer);
718
+ const proxyParam = [
719
+ // tokenAddress
720
+ fromToken.contractAddress,
721
+ // recipient
722
+ depositAddress,
723
+ // amount
724
+ amountWei
725
+ ];
726
+ execTime.breakpoint();
727
+ const mergedCalls = [
728
+ this.allowance({
729
+ dry,
730
+ contractAddress: fromToken.contractAddress,
731
+ address: refundTo,
732
+ spender: proxyAddress,
733
+ amountWei,
734
+ provider
735
+ }),
736
+ this.estimateTransaction({
737
+ dry,
738
+ contract: proxyContract,
739
+ method: "proxyTransfer",
740
+ param: proxyParam,
741
+ fromToken,
742
+ prices,
743
+ evmGasFees
744
+ })
745
+ ];
746
+ const [allowance, ett] = await Promise.all(mergedCalls);
747
+ result.needApprove = allowance.needApprove;
748
+ result.approveSpender = proxyAddress;
749
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
750
+ result.estimateSourceGas = ett.estimateSourceGas;
751
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
752
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
753
+ proxyParam.push({ gasLimit: ett.estimateSourceGasLimit });
754
+ execTime.log("allowance & estimateTransaction");
755
+ result.sendParam = {
756
+ method: "proxyTransfer",
757
+ contract: proxyContract,
758
+ param: proxyParam
759
+ };
760
+ execTime.logTotal("quoteOneClickPorxy");
761
+ return result;
762
+ }
763
+ async quoteNative(params) {
764
+ const {
765
+ quoteResponse,
766
+ bridgeRouterAddress,
767
+ ...restParams
768
+ } = params;
769
+ const {
770
+ dry,
771
+ amountWei,
772
+ refundTo,
773
+ fromToken,
774
+ toToken,
775
+ prices,
776
+ evmGasFees
777
+ } = restParams;
778
+ const result = {
779
+ ...quoteResponse,
780
+ fees: {},
781
+ needApprove: false,
782
+ approveSpender: bridgeRouterAddress,
783
+ quoteParam: {
784
+ ...restParams
785
+ },
786
+ sendParam: {
787
+ txRequest: quoteResponse.txRequest
788
+ },
789
+ totalFeesUsd: void 0,
790
+ estimateSourceGas: void 0,
791
+ totalEstimateSourceGas: 0n,
792
+ estimateSourceGasUsd: void 0,
793
+ estimateTime: quoteResponse.priority === "fast" ? Math.floor(Math.random() * 10) + 50 : Math.floor(Math.random() * 60) + 300,
794
+ outputAmount: 0
795
+ };
796
+ const execTime = new core.ExecTime({ type: `Native EVM ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-400", isDebug: core.OpenAPI.DEBUG });
797
+ utilsEvm.evmRpcFallbackProvider(fromToken);
798
+ const estimateNativeGas = async () => {
799
+ if (dry) {
800
+ const ett = await this.estimateTransaction({
801
+ dry,
802
+ fromToken,
803
+ prices,
804
+ evmGasFees
805
+ });
806
+ result.outputAmount = quoteResponse.buyerTokenAmount + "";
807
+ result.fees = {
808
+ estimateGasUsd: ett.estimateSourceGasUsd,
809
+ widgetFeeUsd: quoteResponse.widgetFeeUsd,
810
+ liquidityProviderFeeUsd: quoteResponse.liquidityProviderFeeUsd
811
+ };
812
+ result.estimateSourceGas = ett.estimateSourceGas;
813
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
814
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
815
+ result.totalFeesUsd = core.numberRemoveEndZero(Big__default.default(quoteResponse.totalFeeUsd).toFixed(20));
816
+ } else {
817
+ const ett = await this.estimateTransaction({
818
+ dry,
819
+ fromToken,
820
+ prices,
821
+ evmGasFees,
822
+ txRequest: quoteResponse.txRequest,
823
+ refundTo
824
+ });
825
+ result.outputAmount = Big__default.default(quoteResponse.amountOut || 0).div(10 ** (toToken.decimals || 6)).toFixed(toToken.decimals || 6, 0);
826
+ result.fees = {
827
+ estimateGasUsd: ett.estimateSourceGasUsd,
828
+ // maybe 100 = 1%
829
+ widgetFeeUsd: core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** (fromToken.decimals || 6)).times(quoteResponse.widgetFee.feeRate || 0).toFixed(20, 0)),
830
+ // not return by api
831
+ liquidityProviderFeeUsd: "0"
832
+ };
833
+ result.estimateSourceGas = ett.estimateSourceGas;
834
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
835
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
836
+ result.totalFeesUsd = core.numberRemoveEndZero(Big__default.default(quoteResponse.amountOutBeforeFee || 0).minus(quoteResponse.amountOut).div(10 ** (fromToken.decimals || 6)).toFixed(20, 0));
837
+ result.txRequest.gasLimit = ett.estimateSourceGasLimit;
838
+ }
839
+ };
840
+ execTime.breakpoint();
841
+ const mergedCalls = [
842
+ this.allowance({
843
+ dry,
844
+ contractAddress: fromToken.contractAddress,
845
+ spender: bridgeRouterAddress,
846
+ address: refundTo,
847
+ amountWei
848
+ }),
849
+ estimateNativeGas()
850
+ ];
851
+ const [allowanceResult] = await Promise.all(mergedCalls);
852
+ result.needApprove = allowanceResult.needApprove;
853
+ execTime.log("allowance & estimateNativeGas");
854
+ execTime.logTotal("quoteNative");
855
+ return result;
856
+ }
857
+ async quoteFraxZero(params) {
858
+ const {
859
+ dry,
860
+ abi,
861
+ recipient,
862
+ amountWei,
863
+ slippageTolerance,
864
+ fromToken,
865
+ toToken,
866
+ prices,
867
+ evmGasFees,
868
+ excludeFees,
869
+ refundTo,
870
+ originLayerzero,
871
+ destinationLayerzero
872
+ } = params;
873
+ const {
874
+ eid: srcEid,
875
+ remoteHop,
876
+ lockbox
877
+ } = originLayerzero;
878
+ const {
879
+ eid: dstEid
880
+ } = destinationLayerzero;
881
+ const isFromEthereum = fromToken.chainId === 1;
882
+ const result = {
883
+ needApprove: false,
884
+ approveSpender: remoteHop,
885
+ sendParam: void 0,
886
+ quoteParam: {
887
+ ...params
888
+ },
889
+ fees: {},
890
+ totalFeesUsd: 0,
891
+ estimateSourceGas: 0n,
892
+ totalEstimateSourceGas: 0n,
893
+ estimateSourceGasUsd: 0,
894
+ estimateTime: 0,
895
+ outputAmount: core.numberRemoveEndZero(Big__default.default(amountWei || 0).div(10 ** params.fromToken.decimals).toFixed(params.fromToken.decimals, 0))
896
+ };
897
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
898
+ const execTime = new core.ExecTime({ type: `FraxZero EVM ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-500", isDebug: core.OpenAPI.DEBUG });
899
+ const remoteHopContract = new ethers.ethers.Contract(remoteHop, abi, this.signer);
900
+ const remoteHopContractRead = new ethers.ethers.Contract(remoteHop, abi, provider);
901
+ const sendParams = [
902
+ // _oft
903
+ isFromEthereum ? lockbox : fromToken.contractAddress,
904
+ // _dstEid
905
+ dstEid,
906
+ // _to
907
+ bridges.addressToBytes32(toToken.chainType, recipient),
908
+ // _amountLD
909
+ amountWei
910
+ ];
911
+ execTime.breakpoint();
912
+ const mergedCalls = [
913
+ this.allowance({
914
+ dry,
915
+ contractAddress: fromToken.contractAddress,
916
+ spender: remoteHop,
917
+ address: refundTo,
918
+ amountWei,
919
+ provider
920
+ }),
921
+ remoteHopContractRead.quote.staticCall(...sendParams)
922
+ ];
923
+ const [allowanceResult, msgFee] = await Promise.all(mergedCalls);
924
+ execTime.log("allowance & quote.staticCall");
925
+ result.needApprove = allowanceResult.needApprove;
926
+ let nativeMsgFee = msgFee[0];
927
+ this.csl("EVM quoteFraxZero", "blue-700", "nativeMsgFee: %o", nativeMsgFee);
928
+ nativeMsgFee = nativeMsgFee * utilsEvm.NATIVE_MSG_FEE_BUFFER / 100n;
929
+ this.csl("EVM quoteFraxZero", "blue-700", "nativeMsgFee after buffer: %o", nativeMsgFee);
930
+ result.totalEstimateSourceGas = nativeMsgFee;
931
+ const nativeFeeUsd = Big__default.default(nativeMsgFee?.toString() || 0).div(10 ** fromToken.nativeToken.decimals).times(core.getPrice(prices, fromToken.nativeToken.symbol));
932
+ result.fees.nativeFee = core.numberRemoveEndZero(Big__default.default(nativeMsgFee?.toString() || 0).div(10 ** fromToken.nativeToken.decimals).toFixed(fromToken.nativeToken.decimals));
933
+ result.fees.nativeFeeUsd = core.numberRemoveEndZero(Big__default.default(nativeFeeUsd).toFixed(20));
934
+ result.fees.lzTokenFeeUsd = core.numberRemoveEndZero(Big__default.default(msgFee[1]?.toString() || 0).div(10 ** fromToken.decimals).toFixed(20));
935
+ this.csl("EVM quoteFraxZero", "blue-700", "msgFee: %o", msgFee);
936
+ execTime.breakpoint();
937
+ const finalSendParam = [...sendParams, { value: nativeMsgFee }];
938
+ const ett = await this.estimateTransaction({
939
+ dry,
940
+ contract: remoteHopContract,
941
+ method: "sendOFT",
942
+ param: finalSendParam,
943
+ fromToken,
944
+ prices,
945
+ evmGasFees
946
+ });
947
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
948
+ result.estimateSourceGas = ett.estimateSourceGas;
949
+ result.totalEstimateSourceGas += ett.estimateSourceGas;
950
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
951
+ finalSendParam[finalSendParam.length - 1].gasLimit = ett.estimateSourceGasLimit;
952
+ execTime.log("sendOFT.estimateTransaction");
953
+ result.sendParam = {
954
+ contract: remoteHopContract,
955
+ method: "sendOFT",
956
+ param: finalSendParam
957
+ };
958
+ for (const feeKey in result.fees) {
959
+ if (excludeFees.includes(feeKey) || !/Usd$/.test(feeKey)) {
960
+ continue;
961
+ }
962
+ result.totalFeesUsd = Big__default.default(result.totalFeesUsd || 0).plus(result.fees[feeKey] || 0);
963
+ }
964
+ result.totalFeesUsd = core.numberRemoveEndZero(Big__default.default(result.totalFeesUsd).toFixed(20));
965
+ execTime.logTotal("quoteFraxZero");
966
+ return result;
967
+ }
968
+ async preivewRedeemFrxUSD(params) {
969
+ const {
970
+ dry,
971
+ amountWei,
972
+ fromToken,
973
+ abi,
974
+ usdcCustodianAddress,
975
+ rwaCustodianAddress,
976
+ redemptionAddress
977
+ } = params;
978
+ if (dry) {
979
+ return {
980
+ maxUsdc: 0n,
981
+ maxRwa: 0n,
982
+ amountWeiBigInt: 0n,
983
+ // The token obtained from redeem is Ethereum USDC
984
+ totalAssetsOut: BigInt(Big__default.default(amountWei || 0).div(10 ** fromToken.decimals).times(10 ** bridges.FRAXZERO_MIDDLE_TOKEN_USDC.decimals).toFixed(0, 0)),
985
+ isInsufficientLiquidity: false
986
+ };
987
+ }
988
+ const execTime = new core.ExecTime({ type: `FraxZero EVM preivewRedeemFrxUSD ${fromToken.chainName}`, logStyle: "stone-600", isDebug: core.OpenAPI.DEBUG });
989
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
990
+ const usdcCustodian = new ethers.ethers.Contract(usdcCustodianAddress, abi, provider);
991
+ const rwaCustodian = new ethers.ethers.Contract(rwaCustodianAddress, abi, provider);
992
+ const redemption = new ethers.ethers.Contract(redemptionAddress, abi, provider);
993
+ execTime.breakpoint();
994
+ const [usdcView, redemptionView] = await Promise.all([
995
+ usdcCustodian.mdwrComboView.staticCall(),
996
+ redemption.maxUstbRedemptionAmount.staticCall()
997
+ ]);
998
+ execTime.log("usdcCustodian & redemption");
999
+ const maxUsdc = usdcView[3];
1000
+ const superstateTokenAmount = redemptionView[0];
1001
+ const [maxRwa] = await redemption.calculateUsdcOut.staticCall(superstateTokenAmount);
1002
+ const amountWeiBigInt = BigInt(amountWei || 0);
1003
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "usdcCustodian maxSharesRedeemable(frxUSD wei amount): %o", maxUsdc);
1004
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "rwaCustodian maxSharesRedeemable(USDC wei amount): %o", maxRwa);
1005
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "amountWei: %o", amountWeiBigInt);
1006
+ let totalAssetsOut = 0n;
1007
+ let isInsufficientLiquidity = false;
1008
+ if (amountWeiBigInt <= maxUsdc) {
1009
+ execTime.breakpoint();
1010
+ const assetsOut = await usdcCustodian.previewRedeem.staticCall(amountWeiBigInt);
1011
+ execTime.log("usdcCustodian.previewRedeem (USDC only)");
1012
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC path only, usdcCustodian previewRedeem input: %o, value: %o", amountWeiBigInt, assetsOut);
1013
+ totalAssetsOut = assetsOut;
1014
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC path only, totalAssetsOut: %o", totalAssetsOut);
1015
+ } else {
1016
+ if (maxUsdc > 0n) {
1017
+ execTime.breakpoint();
1018
+ const usdcAssetsOut = await usdcCustodian.previewRedeem.staticCall(maxUsdc);
1019
+ execTime.log("usdcCustodian.previewRedeem (mixed)");
1020
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC first (maxUsdc), usdcCustodian previewRedeem input: %o, value: %o", maxUsdc, usdcAssetsOut);
1021
+ totalAssetsOut += usdcAssetsOut;
1022
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC first (maxUsdc), totalAssetsOut: %o", totalAssetsOut);
1023
+ }
1024
+ const rwaAmount = amountWeiBigInt - maxUsdc;
1025
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "RWA for remainder, rwaAmount: %o", rwaAmount);
1026
+ if (rwaAmount > 0n) {
1027
+ execTime.breakpoint();
1028
+ const superstateTokenInAmount = await rwaCustodian.previewRedeem.staticCall(rwaAmount);
1029
+ execTime.log("rwaCustodian.previewRedeem (mixed)");
1030
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC first (maxUsdc), rwaCustodian previewRedeem input: %o, superstateTokenInAmount: %o", rwaAmount, superstateTokenInAmount);
1031
+ execTime.breakpoint();
1032
+ const [rwaAssetsOut] = await redemption.calculateUsdcOut.staticCall(superstateTokenInAmount);
1033
+ execTime.log("redemption.calculateUsdcOut (mixed)");
1034
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "USDC first (maxUsdc), rwaCustodian previewRedeem input: %o, value: %o", rwaAmount, rwaAssetsOut);
1035
+ totalAssetsOut += rwaAssetsOut;
1036
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "RWA for remainder, totalAssetsOut: %o", totalAssetsOut);
1037
+ if (rwaAssetsOut > maxRwa) {
1038
+ isInsufficientLiquidity = true;
1039
+ }
1040
+ }
1041
+ }
1042
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "final totalAssetsOut: %o", totalAssetsOut);
1043
+ this.csl("EVM preivewRedeemFrxUSD", "blue-700", "final insufficient liquidity: %o", isInsufficientLiquidity);
1044
+ execTime.logTotal("preivewRedeemFrxUSD");
1045
+ return {
1046
+ maxUsdc,
1047
+ maxRwa,
1048
+ amountWeiBigInt,
1049
+ totalAssetsOut,
1050
+ isInsufficientLiquidity
1051
+ };
1052
+ }
1053
+ async previewMintFrxUSD(params) {
1054
+ const {
1055
+ dry,
1056
+ amountWei,
1057
+ fromToken,
1058
+ abi,
1059
+ usdcCustodianAddress
1060
+ } = params;
1061
+ this.csl("EVM previewMintFrxUSD", "blue-700", "params: %o", params);
1062
+ if (dry) {
1063
+ return {
1064
+ maxUsdc: 0n,
1065
+ amountWeiBigInt: 0n,
1066
+ totalMax: 0n,
1067
+ // Minting means converting Ethereum USDC into Ethereum frxUSD
1068
+ totalAssetsOut: BigInt(Big__default.default(amountWei || 0).div(10 ** fromToken.decimals).times(10 ** bridges.FRAXZERO_MIDDLE_TOKEN_FRXUSD.decimals).toFixed(0, 0))
1069
+ };
1070
+ }
1071
+ const execTime = new core.ExecTime({ type: `FraxZero EVM previewMintFrxUSD ${fromToken.chainName}`, logStyle: "stone-700", isDebug: core.OpenAPI.DEBUG });
1072
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
1073
+ const usdcCustodian = new ethers.ethers.Contract(usdcCustodianAddress, abi, provider);
1074
+ execTime.breakpoint();
1075
+ const usdcView = await usdcCustodian.mdwrComboView.staticCall();
1076
+ execTime.log("usdcCustodian.mdwrComboView");
1077
+ const maxUsdc = usdcView[0];
1078
+ const amountWeiBigInt = BigInt(amountWei || 0);
1079
+ const totalMax = maxUsdc;
1080
+ execTime.breakpoint();
1081
+ const totalAssetsOut = await usdcCustodian.previewDeposit.staticCall(amountWeiBigInt);
1082
+ execTime.log("usdcCustodian.previewDeposit");
1083
+ execTime.logTotal("previewMintFrxUSD");
1084
+ return {
1085
+ maxUsdc,
1086
+ amountWeiBigInt,
1087
+ totalMax,
1088
+ totalAssetsOut
1089
+ };
1090
+ }
1091
+ /**
1092
+ * Redeem frxUSD to USDC via USDC and/or RWA custodian contracts.
1093
+ * Uses mdwrComboView to get max redeemable liquidity and splits redemption across USDC/RWA paths.
1094
+ * Uses previewRedeem to get accurate output amount (USDC).
1095
+ */
1096
+ async redeemFrxUSD(params) {
1097
+ const {
1098
+ dry,
1099
+ recipient,
1100
+ amountWei,
1101
+ fromToken,
1102
+ toToken,
1103
+ prices,
1104
+ evmGasFees,
1105
+ refundTo,
1106
+ abi,
1107
+ usdcCustodianAddress,
1108
+ rwaCustodianAddress,
1109
+ redeemAndMintContractAddress,
1110
+ preivewRedeemResult
1111
+ } = params;
1112
+ this.csl("EVM redeemFrxUSD", "blue-700", "params: %o", params);
1113
+ const ETH_AVG_BLOCK_TIME = 12;
1114
+ const estimateTime = ETH_AVG_BLOCK_TIME * 2 + Math.floor(Math.random() * 11) - 5;
1115
+ const result = {
1116
+ needApprove: false,
1117
+ approveSpender: redeemAndMintContractAddress,
1118
+ sendParam: void 0,
1119
+ quoteParam: {
1120
+ ...params
1121
+ },
1122
+ fees: {},
1123
+ totalFeesUsd: 0,
1124
+ estimateSourceGas: 0n,
1125
+ totalEstimateSourceGas: 0n,
1126
+ estimateSourceGasUsd: 0,
1127
+ estimateTime,
1128
+ outputAmount: "0"
1129
+ };
1130
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
1131
+ const execTime = new core.ExecTime({ type: `FraxZero EVM redeemFrxUSD ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-800", isDebug: core.OpenAPI.DEBUG });
1132
+ new ethers.ethers.Contract(redeemAndMintContractAddress, abi, provider);
1133
+ const redeemContract = new ethers.ethers.Contract(redeemAndMintContractAddress, abi, this.signer);
1134
+ execTime.breakpoint();
1135
+ let finalPreivewRedeemResult = preivewRedeemResult;
1136
+ if (!finalPreivewRedeemResult) {
1137
+ finalPreivewRedeemResult = await this.preivewRedeemFrxUSD(params);
1138
+ }
1139
+ const {
1140
+ isInsufficientLiquidity,
1141
+ totalAssetsOut
1142
+ } = finalPreivewRedeemResult;
1143
+ execTime.log("preivewRedeemFrxUSD");
1144
+ if (isInsufficientLiquidity) {
1145
+ throw new Error("Insufficient liquidity");
1146
+ }
1147
+ result.outputAmount = core.numberRemoveEndZero(
1148
+ Big__default.default(totalAssetsOut.toString()).div(10 ** toToken.decimals).toFixed(toToken.decimals, 0)
1149
+ );
1150
+ execTime.breakpoint();
1151
+ const allowanceResult = await this.allowance({
1152
+ dry,
1153
+ contractAddress: fromToken.contractAddress,
1154
+ spender: redeemAndMintContractAddress,
1155
+ address: refundTo,
1156
+ amountWei,
1157
+ provider
1158
+ });
1159
+ result.needApprove = allowanceResult.needApprove;
1160
+ execTime.log("allowance");
1161
+ result.sendParam = {
1162
+ contract: redeemContract,
1163
+ method: "redeemToUsdcAndTransfer",
1164
+ param: [
1165
+ amountWei,
1166
+ recipient
1167
+ ]
1168
+ };
1169
+ execTime.breakpoint();
1170
+ const ett = await this.estimateTransaction({
1171
+ dry,
1172
+ contract: redeemContract,
1173
+ method: result.sendParam.method,
1174
+ param: [...result.sendParam.param, { gasLimit: DEFAULT_GAS_LIMIT_FAILED }],
1175
+ fromToken,
1176
+ prices,
1177
+ evmGasFees
1178
+ });
1179
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
1180
+ result.estimateSourceGas = ett.estimateSourceGas;
1181
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
1182
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
1183
+ result.totalFeesUsd = ett.estimateSourceGasUsd;
1184
+ result.sendParam.param[2] = { gasLimit: ett.estimateSourceGasLimit };
1185
+ execTime.log("estimateTransaction");
1186
+ execTime.logTotal("redeemFrxUSD");
1187
+ return result;
1188
+ }
1189
+ /**
1190
+ * Mint frxUSD by depositing USDC into the custodian contract.
1191
+ * Calls deposit(assetsIn, receiver) on usdcCustodianAddress.
1192
+ * assetsIn = USDC amount (6 decimals), sharesOut = frxUSD amount (18 decimals).
1193
+ */
1194
+ async mintFrxUSD(params) {
1195
+ const {
1196
+ dry,
1197
+ recipient,
1198
+ amountWei,
1199
+ fromToken,
1200
+ toToken,
1201
+ prices,
1202
+ evmGasFees,
1203
+ refundTo,
1204
+ abi,
1205
+ usdcCustodianAddress,
1206
+ previewMintResult
1207
+ } = params;
1208
+ this.csl("EVM mintFrxUSD", "blue-700", "params: %o", params);
1209
+ const ETH_AVG_BLOCK_TIME = 12;
1210
+ const estimateTime = ETH_AVG_BLOCK_TIME * 2 + Math.floor(Math.random() * 11) - 5;
1211
+ const result = {
1212
+ needApprove: false,
1213
+ approveSpender: usdcCustodianAddress,
1214
+ sendParam: void 0,
1215
+ quoteParam: {
1216
+ ...params
1217
+ },
1218
+ fees: {},
1219
+ totalFeesUsd: 0,
1220
+ estimateSourceGas: 0n,
1221
+ totalEstimateSourceGas: 0n,
1222
+ estimateSourceGasUsd: 0,
1223
+ estimateTime,
1224
+ outputAmount: "0"
1225
+ };
1226
+ const execTime = new core.ExecTime({ type: `FraxZero EVM mintFrxUSD ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-900", isDebug: core.OpenAPI.DEBUG });
1227
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
1228
+ execTime.breakpoint();
1229
+ const allowanceResult = await this.allowance({
1230
+ dry,
1231
+ contractAddress: fromToken.contractAddress,
1232
+ spender: usdcCustodianAddress,
1233
+ address: refundTo,
1234
+ amountWei,
1235
+ provider
1236
+ });
1237
+ result.needApprove = allowanceResult.needApprove;
1238
+ execTime.log("allowance");
1239
+ new ethers.ethers.Contract(usdcCustodianAddress, abi, provider);
1240
+ const usdcCustodianWithSigner = new ethers.ethers.Contract(usdcCustodianAddress, abi, this.signer);
1241
+ execTime.breakpoint();
1242
+ let finalPreviewMintResult = previewMintResult;
1243
+ if (!finalPreviewMintResult) {
1244
+ finalPreviewMintResult = await this.previewMintFrxUSD(params);
1245
+ }
1246
+ const {
1247
+ maxUsdc,
1248
+ amountWeiBigInt,
1249
+ totalMax,
1250
+ totalAssetsOut
1251
+ } = finalPreviewMintResult;
1252
+ execTime.log("previewMintFrxUSD");
1253
+ result.outputAmount = core.numberRemoveEndZero(
1254
+ Big__default.default(totalAssetsOut.toString()).div(10 ** toToken.decimals).toFixed(toToken.decimals, 0)
1255
+ );
1256
+ result.sendParam = {
1257
+ contract: usdcCustodianWithSigner,
1258
+ method: "deposit",
1259
+ param: [
1260
+ amountWeiBigInt,
1261
+ recipient
1262
+ ]
1263
+ };
1264
+ execTime.breakpoint();
1265
+ const ett = await this.estimateTransaction({
1266
+ dry,
1267
+ contract: usdcCustodianWithSigner,
1268
+ method: "deposit",
1269
+ param: [...result.sendParam.param, { gasLimit: DEFAULT_GAS_LIMIT_FAILED }],
1270
+ fromToken,
1271
+ prices,
1272
+ evmGasFees
1273
+ });
1274
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
1275
+ result.estimateSourceGas = ett.estimateSourceGas;
1276
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
1277
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
1278
+ result.totalFeesUsd = ett.estimateSourceGasUsd;
1279
+ result.sendParam.param[2] = { gasLimit: ett.estimateSourceGasLimit };
1280
+ execTime.log("estimateTransaction");
1281
+ execTime.logTotal("mintFrxUSD");
1282
+ return result;
1283
+ }
1284
+ async mintAndSendFrxUSD(params) {
1285
+ const {
1286
+ dry,
1287
+ recipient,
1288
+ amountWei,
1289
+ fromToken,
1290
+ toToken,
1291
+ prices,
1292
+ evmGasFees,
1293
+ refundTo,
1294
+ abi,
1295
+ usdcCustodianAddress,
1296
+ redeemAndMintContractAddress,
1297
+ originLayerzero,
1298
+ destinationLayerzero,
1299
+ previewMintResult
1300
+ } = params;
1301
+ this.csl("EVM mintAndSendFrxUSD", "blue-700", "params: %o", params);
1302
+ const {
1303
+ eid: dstEid
1304
+ } = destinationLayerzero;
1305
+ const ETH_AVG_BLOCK_TIME = 12;
1306
+ const estimateTime = ETH_AVG_BLOCK_TIME * 2 + Math.floor(Math.random() * 11) - 5;
1307
+ const result = {
1308
+ needApprove: false,
1309
+ approveSpender: redeemAndMintContractAddress,
1310
+ sendParam: void 0,
1311
+ quoteParam: {
1312
+ ...params
1313
+ },
1314
+ fees: {},
1315
+ totalFeesUsd: 0,
1316
+ estimateSourceGas: 0n,
1317
+ totalEstimateSourceGas: 0n,
1318
+ estimateSourceGasUsd: 0,
1319
+ estimateTime,
1320
+ outputAmount: "0"
1321
+ };
1322
+ const execTime = new core.ExecTime({ type: `FraxZero EVM mintAndSendFrxUSD ${fromToken.chainName}->${toToken.chainName}`, logStyle: "stone-950", isDebug: core.OpenAPI.DEBUG });
1323
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
1324
+ execTime.breakpoint();
1325
+ const allowanceResult = await this.allowance({
1326
+ dry,
1327
+ contractAddress: fromToken.contractAddress,
1328
+ spender: redeemAndMintContractAddress,
1329
+ address: refundTo,
1330
+ amountWei,
1331
+ provider
1332
+ });
1333
+ result.needApprove = allowanceResult.needApprove;
1334
+ execTime.log("allowance");
1335
+ const redeemAndMintContractWithSigner = new ethers.ethers.Contract(redeemAndMintContractAddress, abi, this.signer);
1336
+ execTime.breakpoint();
1337
+ let finalPreviewMintResult = previewMintResult;
1338
+ if (!finalPreviewMintResult) {
1339
+ finalPreviewMintResult = await this.previewMintFrxUSD(params);
1340
+ }
1341
+ const {
1342
+ totalAssetsOut
1343
+ } = finalPreviewMintResult;
1344
+ execTime.log("previewMintFrxUSD");
1345
+ this.csl("EVM mintAndSendFrxUSD", "gray-600", "previewMintFrxUSD totalAssetsOut: %o", totalAssetsOut);
1346
+ result.outputAmount = core.numberRemoveEndZero(
1347
+ Big__default.default(totalAssetsOut.toString()).div(10 ** 18).toFixed(18, 0)
1348
+ );
1349
+ const mintAndSendParam = [
1350
+ // assetsIn
1351
+ amountWei,
1352
+ // dstEid
1353
+ dstEid,
1354
+ // receiver
1355
+ bridges.addressToBytes32(toToken.chainType, recipient)
1356
+ ];
1357
+ result.sendParam = {
1358
+ contract: redeemAndMintContractWithSigner,
1359
+ method: "mintAndSend",
1360
+ param: mintAndSendParam
1361
+ };
1362
+ execTime.breakpoint();
1363
+ const ett = await this.estimateTransaction({
1364
+ dry,
1365
+ contract: redeemAndMintContractWithSigner,
1366
+ method: "mintAndSend",
1367
+ param: [...mintAndSendParam, { gasLimit: DEFAULT_GAS_LIMIT_FAILED }],
1368
+ fromToken,
1369
+ prices,
1370
+ evmGasFees
1371
+ });
1372
+ result.fees.estimateGasUsd = ett.estimateSourceGasUsd;
1373
+ result.estimateSourceGas = ett.estimateSourceGas;
1374
+ result.totalEstimateSourceGas = ett.estimateSourceGas;
1375
+ result.estimateSourceGasUsd = ett.estimateSourceGasUsd;
1376
+ result.totalFeesUsd = ett.estimateSourceGasUsd;
1377
+ result.sendParam.param.push({ gasLimit: ett.estimateSourceGasLimit });
1378
+ execTime.log("estimateTransaction");
1379
+ execTime.logTotal("mintAndSendFrxUSD");
1380
+ return result;
1381
+ }
1382
+ async retryLayerzeroLzComponse(params) {
1383
+ const { Options } = await import('@layerzerolabs/lz-v2-utilities');
1384
+ const {
1385
+ layerzeroData,
1386
+ history
1387
+ } = params;
1388
+ const LayerZeroEndpointV2 = "0x1a44076050125825900e736c501f859c50fe728c";
1389
+ const LayerZeroEndpointV2ABI = [
1390
+ {
1391
+ "inputs": [
1392
+ {
1393
+ "internalType": "address",
1394
+ "name": "_from",
1395
+ "type": "address"
1396
+ },
1397
+ {
1398
+ "internalType": "address",
1399
+ "name": "_to",
1400
+ "type": "address"
1401
+ },
1402
+ {
1403
+ "internalType": "bytes32",
1404
+ "name": "_guid",
1405
+ "type": "bytes32"
1406
+ },
1407
+ {
1408
+ "internalType": "uint16",
1409
+ "name": "_index",
1410
+ "type": "uint16"
1411
+ },
1412
+ {
1413
+ "internalType": "bytes",
1414
+ "name": "_message",
1415
+ "type": "bytes"
1416
+ },
1417
+ {
1418
+ "internalType": "bytes",
1419
+ "name": "_extraData",
1420
+ "type": "bytes"
1421
+ }
1422
+ ],
1423
+ "name": "lzCompose",
1424
+ "outputs": [],
1425
+ "stateMutability": "payable",
1426
+ "type": "function"
1427
+ }
1428
+ ];
1429
+ const LayerZeroEndpointV2Contract = new ethers.ethers.Contract(LayerZeroEndpointV2, LayerZeroEndpointV2ABI, this.signer);
1430
+ const toToken = core.usdtTokens[history.destination_chain.blockchain];
1431
+ const amountWei = Big__default.default(history.token_in_amount).times(10 ** (toToken.decimals || 6)).toFixed(0);
1432
+ const originLayerzero = bridges.USDT0_CONFIG["Arbitrum"];
1433
+ const isOriginLegacy = layerzeroData.destination.lzCompose.failedTx[0].from.toLowerCase() === originLayerzero.oftLegacy?.toLowerCase();
1434
+ isOriginLegacy ? originLayerzero.lzReceiveOptionGasLegacy : originLayerzero.lzReceiveOptionGas;
1435
+ const destATA = await utilsSolana.getDestinationAssociatedTokenAddress({
1436
+ recipient: history.receive_address,
1437
+ toToken
1438
+ });
1439
+ if (destATA.needCreateTokenAccount) {
1440
+ bridges.LZ_RECEIVE_VALUE[toToken.chainName] || 0;
1441
+ }
1442
+ const composeFrom = layerzeroData.source.tx.from;
1443
+ const composeMsg = layerzeroData.source.tx.payload.split(composeFrom.replace(/^0x/, ""))[1];
1444
+ const _message = utilsEvm.buildEndpointV2LzComposePayload({
1445
+ nonce: layerzeroData.pathway.nonce,
1446
+ srcEid: layerzeroData.pathway.srcEid,
1447
+ amountLD: amountWei,
1448
+ composeFrom,
1449
+ composeMsg
1450
+ });
1451
+ const contractParams = [
1452
+ layerzeroData.destination.lzCompose.failedTx[0].from,
1453
+ layerzeroData.destination.lzCompose.failedTx[0].to,
1454
+ layerzeroData.guid,
1455
+ layerzeroData.destination.lzCompose.failedTx[0].index,
1456
+ _message,
1457
+ "0x"
1458
+ ];
1459
+ const dstOFT = isOriginLegacy ? originLayerzero.oft : originLayerzero.oftLegacy;
1460
+ const sendParam = {
1461
+ dstEid: bridges.USDT0_CONFIG[history.destination_chain.chainName].eid,
1462
+ to: bridges.addressToBytes32(toToken.chainType, history.receive_address),
1463
+ amountLD: amountWei,
1464
+ minAmountLD: 0n,
1465
+ extraOptions: Options.newOptions().toHex(),
1466
+ composeMsg: "0x",
1467
+ oftCmd: "0x"
1468
+ };
1469
+ const dstOFTContract = new ethers.ethers.Contract(dstOFT, bridges.OFT_ABI, this.provider);
1470
+ const msgFee = await dstOFTContract.quoteSend.staticCall(sendParam, false);
1471
+ let nativeFee = msgFee[0];
1472
+ nativeFee = nativeFee * utilsEvm.NATIVE_MSG_FEE_BUFFER / 100n;
1473
+ const tx = await LayerZeroEndpointV2Contract.lzCompose(
1474
+ ...contractParams,
1475
+ {
1476
+ gasLimit: originLayerzero.composeOptionGas || 8e5,
1477
+ value: nativeFee
1478
+ }
1479
+ );
1480
+ const txReceipt = await tx.wait();
1481
+ if (txReceipt.status === 1) {
1482
+ return txReceipt.hash;
1483
+ }
1484
+ return null;
1485
+ }
1486
+ async signTypedData(params) {
1487
+ const { fromToken, amountWei, spender } = params;
1488
+ this.csl("EVM signTypedData", "blue-900", "params: %o", params);
1489
+ const provider = utilsEvm.evmRpcFallbackProvider(fromToken);
1490
+ const value = amountWei;
1491
+ const tokenAddress = fromToken.contractAddress;
1492
+ const chainId = fromToken.chainId;
1493
+ const deadline = Math.floor(Date.now() / 1e3) + 60 * 60 * 24 * 3;
1494
+ const account = this.signer.address;
1495
+ const erc20 = new ethers.ethers.Contract(tokenAddress, core.erc20Abi, provider);
1496
+ const nonce = await erc20.nonces(account);
1497
+ const name = await erc20.name();
1498
+ let _version = "1";
1499
+ if (fromToken.symbol === "USDC") {
1500
+ _version = "2";
1501
+ }
1502
+ const domain = {
1503
+ name,
1504
+ version: _version,
1505
+ chainId: Number(chainId),
1506
+ verifyingContract: tokenAddress
1507
+ };
1508
+ const types = {
1509
+ Permit: [
1510
+ { name: "owner", type: "address" },
1511
+ { name: "spender", type: "address" },
1512
+ { name: "value", type: "uint256" },
1513
+ { name: "nonce", type: "uint256" },
1514
+ { name: "deadline", type: "uint256" }
1515
+ ]
1516
+ };
1517
+ const values = {
1518
+ owner: account,
1519
+ spender,
1520
+ value,
1521
+ nonce: nonce.toString(),
1522
+ deadline
1523
+ };
1524
+ const signature = await this.signer?.signTypedData(domain, types, values);
1525
+ const { v, r, s } = ethers.ethers.Signature.from(signature);
1526
+ try {
1527
+ const permitParams = [
1528
+ account,
1529
+ spender,
1530
+ value,
1531
+ deadline,
1532
+ v,
1533
+ r,
1534
+ s
1535
+ ];
1536
+ const permitResponse = await erc20.permit.staticCall(...permitParams);
1537
+ this.csl("EVM signTypedData", "green-500", "permit response: %o", permitResponse);
1538
+ } catch (error) {
1539
+ this.csl("EVM signTypedData", "red-500", "check permit signature failed: %o", error);
1540
+ throw new Error("Permit signature verification failed");
1541
+ }
1542
+ return {
1543
+ owner: account,
1544
+ value,
1545
+ deadline,
1546
+ nonce: Number(nonce),
1547
+ v,
1548
+ r,
1549
+ s
1550
+ };
1551
+ }
1552
+ };
1553
+
1554
+ exports.EVMWallet = EVMWallet;
1555
+ exports.default = EVMWallet;
1556
+ //# sourceMappingURL=index.js.map
1557
+ //# sourceMappingURL=index.js.map