@openocean.finance/widget 1.0.28 → 1.0.30

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 (109) 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 +3 -6
  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 +49 -33
  66. package/dist/esm/hooks/useRoutes.js.map +1 -1
  67. package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
  68. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -1
  69. package/dist/esm/hooks/useTokenPrice.js +4 -2
  70. package/dist/esm/hooks/useTokenPrice.js.map +1 -1
  71. package/dist/esm/hooks/useTokens.d.ts +1 -1
  72. package/dist/esm/services/ExecuteRoute.js +142 -124
  73. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  74. package/dist/esm/stores/form/useFieldController.d.ts +1 -1
  75. package/dist/esm/stores/routes/createRouteExecutionStore.js +6 -3
  76. package/dist/esm/stores/routes/createRouteExecutionStore.js.map +1 -1
  77. package/dist/esm/stores/routes/useSetExecutableRoute.d.ts +1 -1
  78. package/dist/esm/types/widget.d.ts +3 -0
  79. package/dist/tsconfig.tsbuildinfo +1 -0
  80. package/package.json +14 -4
  81. package/src/components/AmountInput/AmountInputEndAdornment.tsx +46 -46
  82. package/src/components/Messages/WarningMessages.tsx +7 -2
  83. package/src/components/Step/Step.tsx +37 -31
  84. package/src/components/TransactionDetails.tsx +11 -12
  85. package/src/config/version.ts +1 -1
  86. package/src/cross/adapters/AcrossAdapter.ts +193 -0
  87. package/src/cross/adapters/BaseSwapAdapter.ts +173 -0
  88. package/src/cross/adapters/DebridgeAdapter.ts +375 -0
  89. package/src/cross/adapters/LifiAdapter.ts +213 -0
  90. package/src/cross/adapters/MayanAdapter.ts +179 -0
  91. package/src/cross/adapters/NearIntentsAdapter.ts +539 -0
  92. package/src/cross/adapters/OptimexAdapter.ts +273 -0
  93. package/src/cross/adapters/OrbiterAdapter.ts +270 -0
  94. package/src/cross/adapters/RelayAdapter.ts +248 -0
  95. package/src/cross/adapters/SymbiosisAdapter.ts +144 -0
  96. package/src/cross/adapters/XYFinanceAdapter.ts +213 -0
  97. package/src/cross/adapters/index.ts +9 -0
  98. package/src/cross/constants/index.ts +223 -0
  99. package/src/cross/crossChainQuote.ts +181 -0
  100. package/src/cross/factory.ts +145 -0
  101. package/src/cross/registry.ts +65 -0
  102. package/src/hooks/useGasSufficiencyBridge.ts +1 -3
  103. package/src/hooks/useRouteExecution.ts +2 -1
  104. package/src/hooks/useRoutes.ts +63 -44
  105. package/src/hooks/useSettingMonitor.ts +0 -1
  106. package/src/hooks/useTokenPrice.ts +5 -3
  107. package/src/services/ExecuteRoute.ts +184 -171
  108. package/src/stores/routes/createRouteExecutionStore.ts +13 -4
  109. 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
@@ -181,9 +189,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
181
189
  try {
182
190
  useServerErrorStore.getState().setError(null)
183
191
  const fromAmount = parseUnits(fromTokenAmount, fromToken!.decimals)
184
- const formattedSlippage = slippage
185
- ? (Number.parseFloat(slippage) / 100).toString()
186
- : '0.01' // Default slippage 1%
192
+ const formattedSlippage = slippage ? slippage : '1' // Default slippage 1%
187
193
 
188
194
  let quoteResult: any // Initialize quoteResult
189
195
 
@@ -209,17 +215,29 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
209
215
  chainId: toChainId,
210
216
  }
211
217
 
212
- quoteResult = await DebridgeService.swapUThenCross({
213
- fromMsg: fromMsg,
214
- toMsg: toMsg,
218
+ const walletClient = await getWalletClient(wagmiConfig)
219
+
220
+ quoteResult = await getCrossChainQuote({
221
+ feeBps: 10,
222
+ fromMsg,
223
+ toMsg,
215
224
  inAmount: fromAmount.toString(),
216
- slippage_tolerance: formattedSlippage, // Debridge might use a different format/unit
225
+ slippage_tolerance: formattedSlippage,
217
226
  account: account?.address || '',
218
- receiver: toAddress, // Assuming receiver is the same as account for now
227
+ walletClient,
219
228
  })
229
+
230
+ // quoteResult = await DebridgeService.swapUThenCross({
231
+ // fromMsg: fromMsg,
232
+ // toMsg: toMsg,
233
+ // inAmount: fromAmount.toString(),
234
+ // slippage_tolerance: formattedSlippage, // Debridge might use a different format/unit
235
+ // account: account?.address || '',
236
+ // receiver: toAddress, // Assuming receiver is the same as account for now
237
+ // })
220
238
  // Add a flag or modify structure to indicate it's a Debridge route
221
239
  if (quoteResult) {
222
- quoteResult.isDebridgeRoute = true
240
+ quoteResult.isBridge = true
223
241
  }
224
242
  } else {
225
243
  console.warn(
@@ -260,27 +278,29 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
260
278
  }
261
279
  // Ensure the structure is consistent or add a flag
262
280
  if (quoteResult) {
263
- quoteResult.isDebridgeRoute = false
281
+ quoteResult.isBridge = false
264
282
  }
265
283
  }
266
- const data = quoteResult && quoteResult.data || {}
284
+ const data = (quoteResult && quoteResult.data) || {}
267
285
  // minOutAmount calculation is now handled within DebridgeService or OpenOceanService
268
- const isDebridge = data.isDebridgeRoute === true
269
-
270
- let toAmountMin = '0';
286
+ const isBridge = quoteResult.isBridge
287
+ let toAmountMin = '0'
271
288
  if (data?.minOutAmount) {
272
- toAmountMin = data.minOutAmount;
273
- } else if (isDebridge) {
274
- toAmountMin = '0';
289
+ toAmountMin = data.minOutAmount
290
+ } else if (isBridge) {
291
+ toAmountMin = '0'
275
292
  } 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
- // 如果还是科学计数法,再强制转字符串
293
+ const amount = Number(data?.outAmount || 0)
294
+ const slippageValue = Number.parseFloat(slippage)
295
+ const minAmount = (amount * (100 - slippageValue)) / 100
296
+ toAmountMin = minAmount.toFixed(20).replace(/\.?0+$/, '')
297
+ // If still in scientific notation, force convert to string
281
298
  if (toAmountMin.includes('e') || toAmountMin.includes('E')) {
282
- toAmountMin = minAmount.toLocaleString('fullwide', { useGrouping: false, maximumSignificantDigits: 21 });
283
- toAmountMin = toAmountMin.replace(/\.?0+$/, '');
299
+ toAmountMin = minAmount.toLocaleString('fullwide', {
300
+ useGrouping: false,
301
+ maximumSignificantDigits: 21,
302
+ })
303
+ toAmountMin = toAmountMin.replace(/\.?0+$/, '')
284
304
  }
285
305
  }
286
306
  const route: Route = {
@@ -303,8 +323,8 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
303
323
  steps: [
304
324
  {
305
325
  id: '1',
306
- type: 'openocean',
307
- tool: isDebridge ? 'debridge' : 'openocean',
326
+ type: isBridge ? 'bridge' : 'swap',
327
+ tool: isBridge ? 'bridge' : 'openocean',
308
328
  transactionRequest: {
309
329
  chainId: data?.chainId || fromChainId,
310
330
  from: data?.from,
@@ -312,12 +332,12 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
312
332
  to: data?.to,
313
333
  value: data?.value || '0x0',
314
334
  gasPrice: data?.gasPrice,
315
- type: isDebridge ? '0x0' : data?.dexId || '0x0',
335
+ type: isBridge ? data?.quoteAdapterKey : data?.dexId || '0x0',
316
336
  },
317
337
  toolDetails: {
318
- key: isDebridge ? 'debridge' : 'openocean',
319
- name: isDebridge ? 'Debridge' : 'OpenOcean',
320
- logoURI: isDebridge
338
+ key: isBridge ? data?.quoteAdapterKey : 'openocean',
339
+ name: isBridge ? data?.quoteAdapterName : 'OpenOcean',
340
+ logoURI: isBridge
321
341
  ? 'https://s3.openocean.finance/static/debridge.svg'
322
342
  : 'https://assets.coingecko.com/coins/images/17014/small/ooe_log.png',
323
343
  },
@@ -335,13 +355,11 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
335
355
  fromAmount: fromAmount.toString(),
336
356
  toAmount: data?.outAmount || '0',
337
357
  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',
358
+ approvalAddress: data?.approveContract || '0x0',
359
+ executionDuration:
360
+ data?.executionDuration ||
361
+ Math.floor(Math.random() * 20) + 40,
362
+ tool: isBridge ? 'bridge' : 'openocean',
345
363
  feeCosts: data?.feeCosts || [
346
364
  {
347
365
  name: 'Gas Fee',
@@ -355,6 +373,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
355
373
  ],
356
374
  },
357
375
  includedSteps: [],
376
+ quoteData: isBridge ? data : null,
358
377
  },
359
378
  ],
360
379
  ...(data as any),
@@ -27,7 +27,6 @@ export const useSettingMonitor = () => {
27
27
  const { tools } = useTools()
28
28
  const config = useWidgetConfig()
29
29
  const { setDefaultSettings, resetSettings } = useSettingsActions()
30
-
31
30
  const isSlippageChanged = config.slippage
32
31
  ? Number(slippage) !== config.slippage * 100
33
32
  : 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,