@openocean.finance/widget 1.0.28 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +46 -39
  2. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  3. package/dist/esm/components/Messages/WarningMessages.js +2 -2
  4. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  5. package/dist/esm/components/Step/Step.js +37 -29
  6. package/dist/esm/components/Step/Step.js.map +1 -1
  7. package/dist/esm/components/TransactionDetails.js +2 -5
  8. package/dist/esm/components/TransactionDetails.js.map +1 -1
  9. package/dist/esm/config/version.d.ts +1 -1
  10. package/dist/esm/config/version.js +1 -1
  11. package/dist/esm/cross/adapters/AcrossAdapter.d.ts +15 -0
  12. package/dist/esm/cross/adapters/AcrossAdapter.js +166 -0
  13. package/dist/esm/cross/adapters/AcrossAdapter.js.map +1 -0
  14. package/dist/esm/cross/adapters/BaseSwapAdapter.d.ts +107 -0
  15. package/dist/esm/cross/adapters/BaseSwapAdapter.js +44 -0
  16. package/dist/esm/cross/adapters/BaseSwapAdapter.js.map +1 -0
  17. package/dist/esm/cross/adapters/DebridgeAdapter.d.ts +20 -0
  18. package/dist/esm/cross/adapters/DebridgeAdapter.js +264 -0
  19. package/dist/esm/cross/adapters/DebridgeAdapter.js.map +1 -0
  20. package/dist/esm/cross/adapters/LifiAdapter.d.ts +19 -0
  21. package/dist/esm/cross/adapters/LifiAdapter.js +169 -0
  22. package/dist/esm/cross/adapters/LifiAdapter.js.map +1 -0
  23. package/dist/esm/cross/adapters/MayanAdapter.d.ts +14 -0
  24. package/dist/esm/cross/adapters/MayanAdapter.js +119 -0
  25. package/dist/esm/cross/adapters/MayanAdapter.js.map +1 -0
  26. package/dist/esm/cross/adapters/NearIntentsAdapter.d.ts +21 -0
  27. package/dist/esm/cross/adapters/NearIntentsAdapter.js +425 -0
  28. package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -0
  29. package/dist/esm/cross/adapters/OptimexAdapter.d.ts +19 -0
  30. package/dist/esm/cross/adapters/OptimexAdapter.js +216 -0
  31. package/dist/esm/cross/adapters/OptimexAdapter.js.map +1 -0
  32. package/dist/esm/cross/adapters/OrbiterAdapter.d.ts +20 -0
  33. package/dist/esm/cross/adapters/OrbiterAdapter.js +213 -0
  34. package/dist/esm/cross/adapters/OrbiterAdapter.js.map +1 -0
  35. package/dist/esm/cross/adapters/RelayAdapter.d.ts +14 -0
  36. package/dist/esm/cross/adapters/RelayAdapter.js +171 -0
  37. package/dist/esm/cross/adapters/RelayAdapter.js.map +1 -0
  38. package/dist/esm/cross/adapters/SymbiosisAdapter.d.ts +14 -0
  39. package/dist/esm/cross/adapters/SymbiosisAdapter.js +120 -0
  40. package/dist/esm/cross/adapters/SymbiosisAdapter.js.map +1 -0
  41. package/dist/esm/cross/adapters/XYFinanceAdapter.d.ts +14 -0
  42. package/dist/esm/cross/adapters/XYFinanceAdapter.js +177 -0
  43. package/dist/esm/cross/adapters/XYFinanceAdapter.js.map +1 -0
  44. package/dist/esm/cross/adapters/index.d.ts +2 -0
  45. package/dist/esm/cross/adapters/index.js +10 -0
  46. package/dist/esm/cross/adapters/index.js.map +1 -0
  47. package/dist/esm/cross/constants/index.d.ts +202 -0
  48. package/dist/esm/cross/constants/index.js +183 -0
  49. package/dist/esm/cross/constants/index.js.map +1 -0
  50. package/dist/esm/cross/crossChainQuote.d.ts +25 -0
  51. package/dist/esm/cross/crossChainQuote.js +127 -0
  52. package/dist/esm/cross/crossChainQuote.js.map +1 -0
  53. package/dist/esm/cross/factory.d.ts +9 -0
  54. package/dist/esm/cross/factory.js +125 -0
  55. package/dist/esm/cross/factory.js.map +1 -0
  56. package/dist/esm/cross/registry.d.ts +12 -0
  57. package/dist/esm/cross/registry.js +52 -0
  58. package/dist/esm/cross/registry.js.map +1 -0
  59. package/dist/esm/hooks/useChain.d.ts +1 -1
  60. package/dist/esm/hooks/useGasRefuel.d.ts +1 -1
  61. package/dist/esm/hooks/useGasSufficiencyBridge.js +1 -2
  62. package/dist/esm/hooks/useGasSufficiencyBridge.js.map +1 -1
  63. package/dist/esm/hooks/useRouteExecution.js +2 -1
  64. package/dist/esm/hooks/useRouteExecution.js.map +1 -1
  65. package/dist/esm/hooks/useRoutes.js +50 -32
  66. package/dist/esm/hooks/useRoutes.js.map +1 -1
  67. package/dist/esm/hooks/useSettingMonitor.js +1 -0
  68. package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
  69. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -1
  70. package/dist/esm/hooks/useTokenPrice.js +4 -2
  71. package/dist/esm/hooks/useTokenPrice.js.map +1 -1
  72. package/dist/esm/hooks/useTokens.d.ts +1 -1
  73. package/dist/esm/services/ExecuteRoute.js +142 -124
  74. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  75. package/dist/esm/stores/form/useFieldController.d.ts +1 -1
  76. package/dist/esm/stores/routes/createRouteExecutionStore.js +6 -3
  77. package/dist/esm/stores/routes/createRouteExecutionStore.js.map +1 -1
  78. package/dist/esm/stores/routes/useSetExecutableRoute.d.ts +1 -1
  79. package/dist/esm/types/widget.d.ts +3 -0
  80. package/dist/tsconfig.tsbuildinfo +1 -0
  81. package/package.json +14 -4
  82. package/src/components/AmountInput/AmountInputEndAdornment.tsx +46 -46
  83. package/src/components/Messages/WarningMessages.tsx +7 -2
  84. package/src/components/Step/Step.tsx +37 -31
  85. package/src/components/TransactionDetails.tsx +10 -11
  86. package/src/config/version.ts +1 -1
  87. package/src/cross/adapters/AcrossAdapter.ts +193 -0
  88. package/src/cross/adapters/BaseSwapAdapter.ts +173 -0
  89. package/src/cross/adapters/DebridgeAdapter.ts +375 -0
  90. package/src/cross/adapters/LifiAdapter.ts +213 -0
  91. package/src/cross/adapters/MayanAdapter.ts +179 -0
  92. package/src/cross/adapters/NearIntentsAdapter.ts +539 -0
  93. package/src/cross/adapters/OptimexAdapter.ts +273 -0
  94. package/src/cross/adapters/OrbiterAdapter.ts +270 -0
  95. package/src/cross/adapters/RelayAdapter.ts +248 -0
  96. package/src/cross/adapters/SymbiosisAdapter.ts +144 -0
  97. package/src/cross/adapters/XYFinanceAdapter.ts +213 -0
  98. package/src/cross/adapters/index.ts +9 -0
  99. package/src/cross/constants/index.ts +223 -0
  100. package/src/cross/crossChainQuote.ts +181 -0
  101. package/src/cross/factory.ts +145 -0
  102. package/src/cross/registry.ts +65 -0
  103. package/src/hooks/useGasSufficiencyBridge.ts +1 -3
  104. package/src/hooks/useRouteExecution.ts +2 -1
  105. package/src/hooks/useRoutes.ts +64 -43
  106. package/src/hooks/useSettingMonitor.ts +1 -1
  107. package/src/hooks/useTokenPrice.ts +5 -3
  108. package/src/services/ExecuteRoute.ts +184 -171
  109. package/src/stores/routes/createRouteExecutionStore.ts +13 -4
  110. package/src/types/widget.ts +3 -0
@@ -2,14 +2,17 @@ import { useAccount } from '@openocean.finance/wallet-management'
2
2
  import type { Route } from '@openocean.finance/widget-sdk'
3
3
  import { OpenOceanErrorCode } from '@openocean.finance/widget-sdk'
4
4
  import { useQuery, useQueryClient } from '@tanstack/react-query'
5
- import { parseUnits, formatUnits } from 'viem'
5
+ import { parseUnits } from 'viem'
6
+ import { useConfig } from 'wagmi'
7
+ import { getWalletClient } from 'wagmi/actions'
8
+ import { getCrossChainQuote } from '../cross/crossChainQuote.js'
6
9
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
7
- import { DebridgeService } from '../services/DebridgeService.js'
8
10
  import { OpenOceanService } from '../services/OpenOceanService.js'
9
11
  import { useFieldValues } from '../stores/form/useFieldValues.js'
10
12
  import { useSetExecutableRoute } from '../stores/routes/useSetExecutableRoute.js'
11
13
  import { useSettings } from '../stores/settings/useSettings.js'
12
14
  import { defaultSlippage } from '../stores/settings/useSettingsStore.js'
15
+ import { useServerErrorStore } from '../stores/useServerErrorStore.js'
13
16
  import { WidgetEvent } from '../types/events.js'
14
17
  import { getChainTypeFromAddress } from '../utils/chainType.js'
15
18
  import { useChain } from './useChain.js'
@@ -20,7 +23,6 @@ import { useIsBatchingSupported } from './useIsBatchingSupported.js'
20
23
  import { useSwapOnly } from './useSwapOnly.js'
21
24
  import { useToken } from './useToken.js'
22
25
  import { useWidgetEvents } from './useWidgetEvents.js'
23
- import { useServerErrorStore } from '../stores/useServerErrorStore.js'
24
26
 
25
27
  const refetchTime = 60_000
26
28
 
@@ -29,6 +31,8 @@ interface RoutesProps {
29
31
  }
30
32
 
31
33
  export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
34
+ const wagmiConfig = useConfig()
35
+
32
36
  const {
33
37
  subvariant,
34
38
  sdkConfig,
@@ -38,7 +42,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
38
42
  fee,
39
43
  feeConfig,
40
44
  useRelayerRoutes,
41
- referrer
45
+ referrer,
42
46
  } = useWidgetConfig()
43
47
  const setExecutableRoute = useSetExecutableRoute()
44
48
  const queryClient = useQueryClient()
@@ -96,14 +100,18 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
96
100
  const contractCallQuoteEnabled: boolean =
97
101
  subvariant === 'custom' ? Boolean(contractCalls && account.address) : true
98
102
 
99
- const toAddress = fromChainId === toChainId || (fromChain?.chainType === 'EVM' && toChain?.chainType === 'EVM') ? account.address : _toAddress
100
- // When we bridge between ecosystems we need to be sure toAddress is set and has the same chainType as toChain
103
+ const toAddress =
104
+ fromChainId === toChainId ||
105
+ (fromChain?.chainType === 'EVM' && toChain?.chainType === 'EVM')
106
+ ? account.address
107
+ : _toAddress
108
+ // When bridging between ecosystems, we need to ensure toAddress is set and has the same chainType as toChain
101
109
  // If toAddress is set, it must have the same chainType as toChain
102
110
  const hasToAddressAndChainTypeSatisfied: boolean =
103
111
  !!toChain &&
104
112
  !!toAddress &&
105
113
  getChainTypeFromAddress(toAddress) === toChain.chainType
106
- // We need to check for toAddress only if it is set
114
+ // We only need to check if toAddress is set
107
115
  const isToAddressSatisfied = toAddress
108
116
  ? hasToAddressAndChainTypeSatisfied
109
117
  : true
@@ -182,8 +190,8 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
182
190
  useServerErrorStore.getState().setError(null)
183
191
  const fromAmount = parseUnits(fromTokenAmount, fromToken!.decimals)
184
192
  const formattedSlippage = slippage
185
- ? (Number.parseFloat(slippage) / 100).toString()
186
- : '0.01' // Default slippage 1%
193
+ ? slippage
194
+ : '1' // Default slippage 1%
187
195
 
188
196
  let quoteResult: any // Initialize quoteResult
189
197
 
@@ -209,17 +217,29 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
209
217
  chainId: toChainId,
210
218
  }
211
219
 
212
- quoteResult = await DebridgeService.swapUThenCross({
213
- fromMsg: fromMsg,
214
- toMsg: toMsg,
220
+ const walletClient = await getWalletClient(wagmiConfig)
221
+
222
+ quoteResult = await getCrossChainQuote({
223
+ feeBps: 10,
224
+ fromMsg,
225
+ toMsg,
215
226
  inAmount: fromAmount.toString(),
216
- slippage_tolerance: formattedSlippage, // Debridge might use a different format/unit
227
+ slippage_tolerance: formattedSlippage,
217
228
  account: account?.address || '',
218
- receiver: toAddress, // Assuming receiver is the same as account for now
229
+ walletClient,
219
230
  })
231
+
232
+ // quoteResult = await DebridgeService.swapUThenCross({
233
+ // fromMsg: fromMsg,
234
+ // toMsg: toMsg,
235
+ // inAmount: fromAmount.toString(),
236
+ // slippage_tolerance: formattedSlippage, // Debridge might use a different format/unit
237
+ // account: account?.address || '',
238
+ // receiver: toAddress, // Assuming receiver is the same as account for now
239
+ // })
220
240
  // Add a flag or modify structure to indicate it's a Debridge route
221
241
  if (quoteResult) {
222
- quoteResult.isDebridgeRoute = true
242
+ quoteResult.isBridge = true
223
243
  }
224
244
  } else {
225
245
  console.warn(
@@ -260,27 +280,29 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
260
280
  }
261
281
  // Ensure the structure is consistent or add a flag
262
282
  if (quoteResult) {
263
- quoteResult.isDebridgeRoute = false
283
+ quoteResult.isBridge = false
264
284
  }
265
285
  }
266
- const data = quoteResult && quoteResult.data || {}
286
+ const data = (quoteResult && quoteResult.data) || {}
267
287
  // minOutAmount calculation is now handled within DebridgeService or OpenOceanService
268
- const isDebridge = data.isDebridgeRoute === true
269
-
270
- let toAmountMin = '0';
288
+ const isBridge = quoteResult.isBridge
289
+ let toAmountMin = '0'
271
290
  if (data?.minOutAmount) {
272
- toAmountMin = data.minOutAmount;
273
- } else if (isDebridge) {
274
- toAmountMin = '0';
291
+ toAmountMin = data.minOutAmount
292
+ } else if (isBridge) {
293
+ toAmountMin = '0'
275
294
  } else {
276
- const amount = Number(data?.outAmount || 0);
277
- const slippageValue = Number.parseFloat(slippage);
278
- const minAmount = (amount * (100 - slippageValue)) / 100;
279
- toAmountMin = minAmount.toFixed(20).replace(/\.?0+$/, '');
280
- // 如果还是科学计数法,再强制转字符串
295
+ const amount = Number(data?.outAmount || 0)
296
+ const slippageValue = Number.parseFloat(slippage)
297
+ const minAmount = (amount * (100 - slippageValue)) / 100
298
+ toAmountMin = minAmount.toFixed(20).replace(/\.?0+$/, '')
299
+ // If still in scientific notation, force convert to string
281
300
  if (toAmountMin.includes('e') || toAmountMin.includes('E')) {
282
- toAmountMin = minAmount.toLocaleString('fullwide', { useGrouping: false, maximumSignificantDigits: 21 });
283
- toAmountMin = toAmountMin.replace(/\.?0+$/, '');
301
+ toAmountMin = minAmount.toLocaleString('fullwide', {
302
+ useGrouping: false,
303
+ maximumSignificantDigits: 21,
304
+ })
305
+ toAmountMin = toAmountMin.replace(/\.?0+$/, '')
284
306
  }
285
307
  }
286
308
  const route: Route = {
@@ -303,8 +325,8 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
303
325
  steps: [
304
326
  {
305
327
  id: '1',
306
- type: 'openocean',
307
- tool: isDebridge ? 'debridge' : 'openocean',
328
+ type: isBridge ? 'bridge' : 'swap',
329
+ tool: isBridge ? 'bridge' : 'openocean',
308
330
  transactionRequest: {
309
331
  chainId: data?.chainId || fromChainId,
310
332
  from: data?.from,
@@ -312,12 +334,12 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
312
334
  to: data?.to,
313
335
  value: data?.value || '0x0',
314
336
  gasPrice: data?.gasPrice,
315
- type: isDebridge ? '0x0' : data?.dexId || '0x0',
337
+ type: isBridge ? data?.quoteAdapterKey : data?.dexId || '0x0',
316
338
  },
317
339
  toolDetails: {
318
- key: isDebridge ? 'debridge' : 'openocean',
319
- name: isDebridge ? 'Debridge' : 'OpenOcean',
320
- logoURI: isDebridge
340
+ key: isBridge ? data?.quoteAdapterKey : 'openocean',
341
+ name: isBridge ? data?.quoteAdapterName : 'OpenOcean',
342
+ logoURI: isBridge
321
343
  ? 'https://s3.openocean.finance/static/debridge.svg'
322
344
  : 'https://assets.coingecko.com/coins/images/17014/small/ooe_log.png',
323
345
  },
@@ -335,13 +357,11 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
335
357
  fromAmount: fromAmount.toString(),
336
358
  toAmount: data?.outAmount || '0',
337
359
  toAmountMin,
338
- approvalAddress:
339
- data?.approveContract ||
340
- (isDebridge
341
- ? data?.to
342
- : '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64'),
343
- executionDuration: data?.executionDuration || Math.floor(Math.random() * 20) + 40,
344
- tool: isDebridge ? 'debridge' : 'openocean',
360
+ approvalAddress: data?.approveContract || '0x0',
361
+ executionDuration:
362
+ data?.executionDuration ||
363
+ Math.floor(Math.random() * 20) + 40,
364
+ tool: isBridge ? 'bridge' : 'openocean',
345
365
  feeCosts: data?.feeCosts || [
346
366
  {
347
367
  name: 'Gas Fee',
@@ -355,6 +375,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
355
375
  ],
356
376
  },
357
377
  includedSteps: [],
378
+ quoteData: isBridge ? data : null,
358
379
  },
359
380
  ],
360
381
  ...(data as any),
@@ -27,7 +27,7 @@ export const useSettingMonitor = () => {
27
27
  const { tools } = useTools()
28
28
  const config = useWidgetConfig()
29
29
  const { setDefaultSettings, resetSettings } = useSettingsActions()
30
-
30
+ debugger;
31
31
  const isSlippageChanged = config.slippage
32
32
  ? Number(slippage) !== config.slippage * 100
33
33
  : slippage !== defaultConfigurableSettings.slippage
@@ -9,11 +9,13 @@ export const useTokenPrice = (chainId?: number, token?: TokenAmount) => {
9
9
  if (!chainId || !token?.address) {
10
10
  return undefined
11
11
  }
12
- const prices = await OpenOceanService.getTokensPrice(chainId.toString(), [token.address])
12
+ const prices = await OpenOceanService.getTokensPrice(chainId.toString(), [
13
+ token.address,
14
+ ])
13
15
  return prices[token.address.toLowerCase()] || 0
14
16
  },
15
17
  enabled: !!chainId && !!token?.address,
16
- refetchInterval: 60_000, // 每分钟更新一次价格
18
+ refetchInterval: 60_000, // Update price every minute
17
19
  staleTime: 60_000,
18
20
  })
19
21
 
@@ -21,4 +23,4 @@ export const useTokenPrice = (chainId?: number, token?: TokenAmount) => {
21
23
  price,
22
24
  isLoading,
23
25
  }
24
- }
26
+ }
@@ -8,6 +8,7 @@ import type {
8
8
  import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js'
9
9
  import { ethers } from 'ethers'
10
10
  import { getPublicClient, getWalletClient } from 'wagmi/actions'
11
+ import { bridgeExecuteSwap } from '../cross/crossChainQuote.js'
11
12
  import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
12
13
  import { sendAndConfirmSolanaTransaction } from './SendAndConfirmSolanaTransaction.js'
13
14
 
@@ -123,6 +124,121 @@ function hexToUint8Array(hexString: string): Uint8Array {
123
124
  return new Uint8Array(pairs.map((s) => Number.parseInt(s, 16)))
124
125
  }
125
126
 
127
+ /**
128
+ * Convert amount with precision to actual amount
129
+ * @param amount Amount with precision
130
+ * @param decimals Precision
131
+ * @returns Actual amount
132
+ */
133
+ function decimals2Amount(amount: string | number, decimals = 18): number {
134
+ return Number(amount) / 10 ** decimals
135
+ }
136
+
137
+ /**
138
+ * Swap response type
139
+ */
140
+ interface SwapResponse {
141
+ inAmount?: string
142
+ inToken?: {
143
+ decimals?: number
144
+ price?: string | number
145
+ priceUSD?: string | number
146
+ address?: string
147
+ }
148
+ data?: string
149
+ from?: string
150
+ to?: string
151
+ value?: string
152
+ minOutAmount?: string
153
+ }
154
+
155
+ /**
156
+ * Adjust transaction parameters based on dynamic slippage to provide MEV protection
157
+ */
158
+ async function swapQuoteMEV(
159
+ response: SwapResponse,
160
+ options?: { publicClient?: any }
161
+ ): Promise<SwapResponse> {
162
+ try {
163
+ const inAmount = response?.inAmount
164
+ const inTokenDecimals = response?.inToken?.decimals || 18
165
+ const inTokenPrice = Number(response?.inToken?.priceUSD || 0)
166
+ const amount = decimals2Amount(inAmount, inTokenDecimals) * inTokenPrice
167
+ if (amount < 1) {
168
+ return response
169
+ }
170
+ const { publicClient } = options
171
+ const OPENOCEAN_CONTRACT = new ethers.Contract(
172
+ '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64',
173
+ OpenOceanABI
174
+ )
175
+ if (
176
+ ethers.hexlify(ethers.getBytes(response?.data || '0x').slice(0, 4)) !==
177
+ OPENOCEAN_CONTRACT.interface.getFunction('swap').selector
178
+ ) {
179
+ return response
180
+ }
181
+ const oldCallData = OPENOCEAN_CONTRACT.interface.decodeFunctionData(
182
+ 'swap',
183
+ response?.data
184
+ )
185
+ const callData = [...oldCallData]
186
+ callData[1] = [...oldCallData[1]]
187
+ const minOutAmount = BigInt(callData[1][5] || 0)
188
+ const outAmount = BigInt(callData[1][6] || 0)
189
+ const slippageAmount = outAmount - minOutAmount
190
+
191
+ const minOutAmounts = await Promise.all(
192
+ [1, 2, 3].map(async (i) => {
193
+ const mockMinOutAmount =
194
+ minOutAmount + (slippageAmount / 4n) * BigInt(i)
195
+ callData[1][5] = mockMinOutAmount
196
+ const params = {
197
+ from: response?.from as `0x${string}`,
198
+ to: response?.to as `0x${string}`,
199
+ data: OPENOCEAN_CONTRACT.interface.encodeFunctionData(
200
+ 'swap',
201
+ callData
202
+ ) as `0x${string}`,
203
+ value: BigInt(response?.value || '0'),
204
+ }
205
+ try {
206
+ await publicClient.estimateGas(params)
207
+ return mockMinOutAmount
208
+ } catch (error) {
209
+ console.error('Failed to estimate gas:', error)
210
+ return undefined
211
+ }
212
+ })
213
+ )
214
+ let [min1, min2] = minOutAmounts
215
+ .filter((value) => value !== undefined)
216
+ .sort((a, b) => (BigInt(b || 0) > BigInt(a || 0) ? 1 : -1))
217
+ .slice(0, 2)
218
+ min1 = min1 ?? minOutAmount
219
+ min2 = min2 ?? minOutAmount
220
+
221
+ const randomFactor = BigInt(Math.floor(Math.random() * 10000))
222
+ const minOutAmountDiff = BigInt(min1 || 0) - BigInt(min2 || 0)
223
+ const finalMinOutAmount =
224
+ BigInt(min2 || 0) + (minOutAmountDiff * randomFactor) / BigInt(10000)
225
+
226
+ if (finalMinOutAmount < minOutAmount) {
227
+ return response
228
+ }
229
+ callData[1][5] = finalMinOutAmount
230
+ const finalCallData = OPENOCEAN_CONTRACT.interface.encodeFunctionData(
231
+ 'swap',
232
+ callData
233
+ )
234
+ response.minOutAmount = finalMinOutAmount.toString()
235
+ response.data = finalCallData
236
+ return response
237
+ } catch {
238
+ return response
239
+ }
240
+ }
241
+
126
242
  interface ExecuteRouteOptions {
127
243
  updateRouteHook?: (route: Route) => void
128
244
  acceptExchangeRateUpdateHook?: (params: any) => Promise<boolean>
@@ -135,6 +251,7 @@ interface ExecuteRouteOptions {
135
251
  }
136
252
 
137
253
  interface ExtendedOpenOceanStep extends OpenOceanStep {
254
+ quoteData?: any
138
255
  execution?: {
139
256
  status: ExecutionStatus
140
257
  process: Process[]
@@ -172,7 +289,7 @@ async function executeSolanaSwap(
172
289
  const txData: any = step.transactionRequest?.data || ''
173
290
  const dexId = step.transactionRequest?.type || 0
174
291
  if (step.action.fromChainId === step.action.toChainId) {
175
- if (dexId == 6 || dexId == 7 || dexId == 9) {
292
+ if (dexId === 6 || dexId === 7 || dexId === 9) {
176
293
  transaction = VersionedTransaction.deserialize(hexToUint8Array(txData))
177
294
  } else {
178
295
  transaction = Transaction.from(hexToUint8Array(txData))
@@ -301,22 +418,23 @@ async function executeEvmSwap(
301
418
  throw new Error('Public client not found')
302
419
  }
303
420
 
304
- console.log(
305
- 'Current Chain:',
306
- publicClient.chain?.id,
307
- publicClient.chain?.name
308
- )
309
- console.log('Token Address:', step.action.fromToken.address)
310
- console.log('Token Chain ID:', step.action.fromToken.chainId)
311
- console.log('Owner Address:', walletClient.account.address)
312
- console.log('Spender Address:', step.estimate.approvalAddress)
313
-
421
+ // console.log(
422
+ // 'Current Chain:',
423
+ // publicClient.chain?.id,
424
+ // publicClient.chain?.name
425
+ // )
426
+ // console.log('Token Address:', step.action.fromToken.address)
427
+ // console.log('Token Chain ID:', step.action.fromToken.chainId)
428
+ // console.log('Owner Address:', walletClient.account.address)
429
+ // console.log('Spender Address:', step.estimate.approvalAddress)
314
430
  // Check token approval
315
431
  if (
316
432
  [
317
433
  '0x0000000000000000000000000000000000000000',
318
434
  '0x0000000000000000000000000000000000001010',
319
- ].indexOf(step.action.fromToken.address) === -1
435
+ ].indexOf(step.action.fromToken.address) === -1 &&
436
+ step.estimate.approvalAddress !==
437
+ '0x0000000000000000000000000000000000000000'
320
438
  ) {
321
439
  let allowance = 0n
322
440
  try {
@@ -391,58 +509,68 @@ async function executeEvmSwap(
391
509
  }
392
510
  }
393
511
 
394
- const { transactionRequest } = step || {}
395
- const txRequest = {
396
- chain: publicClient.chain,
397
- to: transactionRequest?.to as `0x${string}`,
398
- data: (transactionRequest?.data as `0x${string}`) || '0x',
399
- value: BigInt(transactionRequest?.value || '0x0'),
400
- account: walletClient.account.address,
401
- }
512
+ const { transactionRequest, type, quoteData } = step || {}
513
+ let hash: any = ''
514
+ if ((type as any) === 'bridge') {
515
+ const result = await bridgeExecuteSwap({
516
+ quoteData: quoteData,
517
+ walletClient: walletClient,
518
+ })
519
+ hash = result.sourceTxHash
520
+ } else {
521
+ const txRequest = {
522
+ chain: publicClient.chain,
523
+ to: transactionRequest?.to as `0x${string}`,
524
+ data: (transactionRequest?.data as `0x${string}`) || '0x',
525
+ value: BigInt(transactionRequest?.value || '0x0'),
526
+ account: walletClient.account.address,
527
+ }
402
528
 
403
- // 在这里根据useSettingsStore 中的 dynamicSlippage 参数来判断是否调用下面的函数 swap_quote_mev
404
- const { dynamicSlippage } = useSettingsStore.getState()
405
- if (dynamicSlippage) {
406
- try {
407
- // 构建 response 对象
408
- const response = {
409
- inAmount: step.action.fromAmount || '0',
410
- inToken: step.action.fromToken,
411
- data: transactionRequest?.data,
412
- from: walletClient.account.address,
413
- to: transactionRequest?.to,
414
- value: transactionRequest?.value || '0',
415
- }
529
+ // Check if dynamicSlippage is enabled in useSettingsStore to determine whether to call swap_quote_mev
530
+ const { dynamicSlippage } = useSettingsStore.getState()
531
+ if (dynamicSlippage) {
532
+ try {
533
+ // Build response object
534
+ const response = {
535
+ inAmount: step.action.fromAmount || '0',
536
+ inToken: step.action.fromToken,
537
+ data: transactionRequest?.data,
538
+ from: walletClient.account.address,
539
+ to: transactionRequest?.to,
540
+ value: transactionRequest?.value || '0',
541
+ }
416
542
 
417
- // 调用 swap_quote_mev 获取调整后的交易数据
418
- const adjustedResponse = await swapQuoteMEV(response, { publicClient })
543
+ // Call swap_quote_mev to get adjusted transaction data
544
+ const adjustedResponse = await swapQuoteMEV(response, {
545
+ publicClient,
546
+ })
419
547
 
420
- // 如果 swap_quote_mev 返回了修改后的数据,更新交易请求
421
- if (adjustedResponse && adjustedResponse.data !== response.data) {
422
- txRequest.data = adjustedResponse.data as `0x${string}`
423
- txRequest.value = BigInt(adjustedResponse.value || '0')
424
- console.log('Applied MEV protection with dynamic slippage')
548
+ // If swap_quote_mev returns modified data, update transaction request
549
+ if (adjustedResponse && adjustedResponse.data !== response.data) {
550
+ txRequest.data = adjustedResponse.data as `0x${string}`
551
+ txRequest.value = BigInt(adjustedResponse.value || '0')
552
+ // Applied MEV protection with dynamic slippage
553
+ }
554
+ } catch (error) {
555
+ console.error('Failed to apply MEV protection:', error)
556
+ // Continue with original transaction request on error
425
557
  }
426
- } catch (error) {
427
- console.error('Failed to apply MEV protection:', error)
428
- // 错误时继续使用原始交易请求
429
558
  }
430
- }
431
559
 
432
- // Estimate gas
433
- const estimatedGas = await publicClient.estimateGas(txRequest)
434
- console.log('estimatedGas', estimatedGas)
560
+ // Estimate gas
561
+ const estimatedGas = await publicClient.estimateGas(txRequest)
435
562
 
436
- // Add estimated gas to transaction request (using 2x the estimated value to ensure transaction success)
437
- const finalTxRequest = {
438
- ...txRequest,
439
- gas: estimatedGas * 2n,
440
- }
563
+ // Add estimated gas to transaction request (using 2x the estimated value to ensure transaction success)
564
+ const finalTxRequest = {
565
+ ...txRequest,
566
+ gas: estimatedGas * 2n,
567
+ }
441
568
 
442
- const hash = await walletClient.sendTransaction({
443
- ...finalTxRequest,
444
- kzg: undefined,
445
- })
569
+ hash = await walletClient.sendTransaction({
570
+ ...finalTxRequest,
571
+ kzg: undefined,
572
+ })
573
+ }
446
574
 
447
575
  process.status = 'PENDING'
448
576
  process.txHash = hash
@@ -485,121 +613,6 @@ async function executeEvmSwap(
485
613
  }
486
614
  }
487
615
 
488
- /**
489
- * 将带精度的金额转换为实际金额
490
- * @param amount 带精度的金额
491
- * @param decimals 精度
492
- * @returns 实际金额
493
- */
494
- function decimals2Amount(amount: string | number, decimals = 18): number {
495
- return Number(amount) / Math.pow(10, decimals)
496
- }
497
-
498
- /**
499
- * 交易响应类型
500
- */
501
- interface SwapResponse {
502
- inAmount?: string
503
- inToken?: {
504
- decimals?: number
505
- price?: string | number
506
- priceUSD?: string | number
507
- address?: string
508
- }
509
- data?: string
510
- from?: string
511
- to?: string
512
- value?: string
513
- minOutAmount?: string
514
- }
515
-
516
- /**
517
- * 根据动态滑点调整交易参数,提供 MEV 保护
518
- */
519
- async function swapQuoteMEV(
520
- response: SwapResponse,
521
- options?: { publicClient?: any }
522
- ): Promise<SwapResponse> {
523
- try {
524
- const inAmount = response?.inAmount
525
- const inTokenDecimals = response?.inToken?.decimals || 18
526
- const inTokenPrice = Number(response?.inToken?.priceUSD || 0)
527
- const amount = decimals2Amount(inAmount, inTokenDecimals) * inTokenPrice
528
- if (amount < 1) {
529
- return response
530
- }
531
- const { publicClient } = options
532
- const OPENOCEAN_CONTRACT = new ethers.Contract(
533
- '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64',
534
- OpenOceanABI
535
- )
536
- if (
537
- ethers.hexlify(ethers.getBytes(response?.data || '0x').slice(0, 4)) !==
538
- OPENOCEAN_CONTRACT.interface.getFunction('swap').selector
539
- ) {
540
- return response
541
- }
542
- const oldCallData = OPENOCEAN_CONTRACT.interface.decodeFunctionData(
543
- 'swap',
544
- response?.data
545
- )
546
- const callData = [...oldCallData]
547
- callData[1] = [...oldCallData[1]]
548
- const minOutAmount = BigInt(callData[1][5] || 0)
549
- const outAmount = BigInt(callData[1][6] || 0)
550
- const slippageAmount = outAmount - minOutAmount
551
-
552
- const minOutAmounts = await Promise.all(
553
- [1, 2, 3].map(async (i) => {
554
- const mockMinOutAmount =
555
- minOutAmount + (slippageAmount / 4n) * BigInt(i)
556
- callData[1][5] = mockMinOutAmount
557
- const params = {
558
- from: response?.from as `0x${string}`,
559
- to: response?.to as `0x${string}`,
560
- data: OPENOCEAN_CONTRACT.interface.encodeFunctionData(
561
- 'swap',
562
- callData
563
- ) as `0x${string}`,
564
- value: BigInt(response?.value || '0'),
565
- }
566
- try {
567
- await publicClient.estimateGas(params)
568
- return mockMinOutAmount
569
- } catch (error) {
570
- console.error('Failed to estimate gas:', error)
571
- return undefined
572
- }
573
- })
574
- )
575
- let [min1, min2] = minOutAmounts
576
- .filter((value) => value !== undefined)
577
- .sort((a, b) => (BigInt(b || 0) > BigInt(a || 0) ? 1 : -1))
578
- .slice(0, 2)
579
- min1 = min1 ?? minOutAmount
580
- min2 = min2 ?? minOutAmount
581
-
582
- const randomFactor = BigInt(Math.floor(Math.random() * 10000))
583
- const minOutAmountDiff = BigInt(min1 || 0) - BigInt(min2 || 0)
584
- const finalMinOutAmount =
585
- BigInt(min2 || 0) + (minOutAmountDiff * randomFactor) / BigInt(10000)
586
-
587
- if (finalMinOutAmount < minOutAmount) {
588
- return response
589
- }
590
- callData[1][5] = finalMinOutAmount
591
- const finalCallData = OPENOCEAN_CONTRACT.interface.encodeFunctionData(
592
- 'swap',
593
- callData
594
- )
595
- response.minOutAmount = finalMinOutAmount.toString()
596
- response.data = finalCallData
597
- return response
598
- } catch (error) {
599
- return response
600
- }
601
- }
602
-
603
616
  // Execute transaction
604
617
  async function executeSwap(
605
618
  route: ExtendedRoute,