@openocean.finance/widget 1.0.52 → 1.0.53

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 (56) hide show
  1. package/dist/esm/AppProvider.js +2 -1
  2. package/dist/esm/AppProvider.js.map +1 -1
  3. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +21 -0
  4. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
  5. package/dist/esm/components/TokenList/VirtualizedTokenList.js +1 -1
  6. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  7. package/dist/esm/components/TransactionDetails.js +6 -0
  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/NearIntentsAdapter.js +94 -25
  12. package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -1
  13. package/dist/esm/cross/crossChainQuote.d.ts +3 -2
  14. package/dist/esm/cross/crossChainQuote.js +2 -2
  15. package/dist/esm/cross/crossChainQuote.js.map +1 -1
  16. package/dist/esm/hooks/useGasPrice.js +2 -2
  17. package/dist/esm/hooks/useGasPrice.js.map +1 -1
  18. package/dist/esm/hooks/useGasRecommendation.d.ts +1 -1
  19. package/dist/esm/hooks/useGasRecommendation.js +7 -1
  20. package/dist/esm/hooks/useGasRecommendation.js.map +1 -1
  21. package/dist/esm/hooks/useRouteExecution.js +5 -0
  22. package/dist/esm/hooks/useRouteExecution.js.map +1 -1
  23. package/dist/esm/hooks/useRoutes.js +1 -1
  24. package/dist/esm/hooks/useRoutes.js.map +1 -1
  25. package/dist/esm/hooks/useToken.js.map +1 -1
  26. package/dist/esm/providers/WalletProvider/NearProvider.d.ts +3 -0
  27. package/dist/esm/providers/WalletProvider/NearProvider.js +95 -0
  28. package/dist/esm/providers/WalletProvider/NearProvider.js.map +1 -0
  29. package/dist/esm/providers/WalletProvider/SDKProviders.js +4 -1
  30. package/dist/esm/providers/WalletProvider/SDKProviders.js.map +1 -1
  31. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +1 -1
  32. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
  33. package/dist/esm/services/ExecuteRoute.d.ts +1 -0
  34. package/dist/esm/services/ExecuteRoute.js +121 -0
  35. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  36. package/dist/esm/services/OpenOceanService.d.ts +5 -1
  37. package/dist/esm/services/OpenOceanService.js +121 -6
  38. package/dist/esm/services/OpenOceanService.js.map +1 -1
  39. package/package.json +9 -5
  40. package/src/AppProvider.tsx +8 -5
  41. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +20 -0
  42. package/src/components/TokenList/VirtualizedTokenList.tsx +1 -1
  43. package/src/components/TransactionDetails.tsx +17 -11
  44. package/src/config/version.ts +1 -1
  45. package/src/cross/adapters/NearIntentsAdapter.ts +100 -28
  46. package/src/cross/crossChainQuote.ts +6 -2
  47. package/src/hooks/useGasPrice.ts +2 -2
  48. package/src/hooks/useGasRecommendation.ts +8 -1
  49. package/src/hooks/useRouteExecution.ts +5 -0
  50. package/src/hooks/useRoutes.ts +1 -1
  51. package/src/hooks/useToken.ts +0 -1
  52. package/src/providers/WalletProvider/NearProvider.tsx +110 -0
  53. package/src/providers/WalletProvider/SDKProviders.tsx +5 -0
  54. package/src/providers/WalletProvider/useExternalWalletProvider.ts +1 -1
  55. package/src/services/ExecuteRoute.ts +134 -1
  56. package/src/services/OpenOceanService.ts +135 -7
@@ -1,5 +1,5 @@
1
1
  import {
2
- type ChainId,
2
+ ChainId,
3
3
  getGasRecommendation,
4
4
  } from '@openocean.finance/widget-sdk'
5
5
  import { useQuery } from '@tanstack/react-query'
@@ -14,9 +14,12 @@ export const useGasRecommendation = (
14
14
  ) => {
15
15
  const { chains } = useAvailableChains()
16
16
 
17
+ const fromIsNear = fromChain === ChainId.NEAR
18
+
17
19
  const checkRecommendationLiFuel =
18
20
  Boolean(toChainId) &&
19
21
  Boolean(fromChain) &&
22
+ !fromIsNear &&
20
23
  Boolean(fromToken) &&
21
24
  Boolean(chains?.length)
22
25
 
@@ -29,6 +32,10 @@ export const useGasRecommendation = (
29
32
  queryKey: [_, toChainId, fromChain, fromToken],
30
33
  signal,
31
34
  }) => {
35
+ // from 是 Near 链时,当前不支持 Li.Fuel / gas 预估,直接跳过查询
36
+ if (fromChain === ChainId.NEAR) {
37
+ return null
38
+ }
32
39
  if (!chains?.some((chain) => chain.id === toChainId)) {
33
40
  return null
34
41
  }
@@ -11,6 +11,8 @@ import { updateRouteExecution } from '@openocean.finance/widget-sdk'
11
11
  import { useMutation, useQueryClient } from '@tanstack/react-query'
12
12
  import { useCallback, useEffect, useRef } from 'react'
13
13
  import { useConfig, useWalletClient } from 'wagmi'
14
+ // @ts-ignore - runtime implementation is provided by the host app (widget package)
15
+ import { useWalletSelector } from '@near-wallet-selector/react-hook'
14
16
  import { shallow } from 'zustand/shallow'
15
17
  import { executeRoute } from '../services/ExecuteRoute.js'
16
18
  import {
@@ -46,6 +48,7 @@ export const useRouteExecution = ({
46
48
  const disconnect = useAccountDisconnect()
47
49
  const { openWalletMenu } = useWalletMenu()
48
50
  const solanaWallet = useWalletClient()
51
+ const nearWallet = useWalletSelector() as any
49
52
  // const { wallet: solanaWallet } = useWallet()
50
53
  const resumedAfterMount = useRef(false)
51
54
  const emitter = useWidgetEvents()
@@ -151,6 +154,7 @@ export const useRouteExecution = ({
151
154
  onDisconnect: disconnect,
152
155
  onOpenWalletMenu: openWalletMenu,
153
156
  solanaWallet: solanaWallet,
157
+ nearWallet,
154
158
  })
155
159
  },
156
160
  onMutate: () => {
@@ -187,6 +191,7 @@ export const useRouteExecution = ({
187
191
  wagmiConfig,
188
192
  onDisconnect: disconnect,
189
193
  onOpenWalletMenu: openWalletMenu,
194
+ nearWallet,
190
195
  })
191
196
  },
192
197
  onMutate: () => {
@@ -303,7 +303,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
303
303
  // minOutAmount calculation is now handled within DebridgeService or OpenOceanService
304
304
  const isBridge = quoteResult.isBridge
305
305
  let toAmountMin = '0'
306
- if (data?.minOutAmount) {
306
+ if (data?.minOutAmount && Number(data.minOutAmount) > 0) {
307
307
  toAmountMin = data.minOutAmount
308
308
  } else if (isBridge) {
309
309
  toAmountMin = '0'
@@ -6,7 +6,6 @@ import type { TokenAmount } from '../types/token.js'
6
6
 
7
7
  export const useToken = (chainId?: number, tokenAddress?: string) => {
8
8
  const { tokens, isLoading } = useTokens(chainId)
9
-
10
9
  const token = useMemo(() => {
11
10
  const token = tokens?.find(
12
11
  (token: TokenAmount) => token.address === tokenAddress && token.chainId === chainId
@@ -0,0 +1,110 @@
1
+ import type { FC, PropsWithChildren } from 'react'
2
+ import { useEffect, useMemo } from 'react'
3
+ import { WalletSelectorProvider } from '@near-wallet-selector/react-hook'
4
+ import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet'
5
+ import { setupSender } from '@near-wallet-selector/sender'
6
+ import "@near-wallet-selector/modal-ui/styles.css";
7
+
8
+
9
+ export const NearProvider: FC<PropsWithChildren> = ({ children }) => {
10
+ const config = useMemo(
11
+ () => ({
12
+ network: 'mainnet' as const,
13
+ modules: [setupMeteorWallet(), setupSender()],
14
+ }),
15
+ []
16
+ )
17
+
18
+ // 在这里注入你自己的全局 CSS,覆盖 Near modal 的样式
19
+ useEffect(() => {
20
+ const styleId = 'near-wallet-selector-custom-style'
21
+ let styleEl = document.getElementById(styleId) as HTMLStyleElement | null
22
+
23
+ if (!styleEl) {
24
+ styleEl = document.createElement('style')
25
+ styleEl.id = styleId
26
+ document.head.appendChild(styleEl)
27
+ }
28
+
29
+ styleEl.textContent = `
30
+ /* 覆盖 Near modal 最外层容器 */
31
+ #near-wallet-selector-modal {
32
+ z-index: 1200 !important; /* 保证在你自己的 widget 之上 */
33
+ font-family: inherit; /* 或者用你自己的字体 */
34
+ }
35
+
36
+ /* 覆盖遮罩层 */
37
+ #near-wallet-selector-modal .nws-modal-overlay {
38
+ background: rgba(0, 0, 0, 0.75) !important;
39
+ }
40
+
41
+ /* 覆盖内容区域,可以调圆角/阴影等 */
42
+ #near-wallet-selector-modal .nws-modal {
43
+ width: 420px !important;
44
+ border-radius: 24px;
45
+ box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);
46
+ background-color: #222037 !important;
47
+ z-index: 1300 !important;
48
+ position: absolute;
49
+ }
50
+ #near-wallet-selector-modal .nws-modal .modal-left {
51
+ border:none !important;
52
+ width: 100% !important;
53
+ }
54
+ #near-wallet-selector-modal .nws-modal .modal-right {
55
+ background-color: #222037 !important;
56
+ display: none;
57
+ }
58
+ .nws-modal-wrapper .nws-modal .modal-left .modal-left-title{
59
+ padding-top: 0 !important;
60
+ }
61
+ .nws-modal-wrapper .nws-modal .modal-left .modal-left-title h2{
62
+ text-align: center;
63
+ color: #ffffff !important;
64
+ }
65
+ .nws-modal-wrapper .nws-modal .modal-left .modal-left-title .nws-remember-wallet{
66
+ display: none;
67
+ }
68
+ .nws-modal-wrapper .nws-modal .modal-left .modal-left-title .nws-switch{
69
+ display: none;
70
+ }
71
+ .nws-modal-wrapper .nws-modal .modal-left .modal-left-title {
72
+ position: relative;
73
+ width: 100%;
74
+ background-color: #222037 !important;
75
+ }
76
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper{
77
+ margin-top: 20px;
78
+ }
79
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper .options-list {
80
+ display: block;
81
+
82
+ }
83
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper .options-list .single-wallet.sidebar{
84
+ margin:10px 0;
85
+ }
86
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper .title{
87
+ color: #ffffff !important;
88
+ }
89
+ .options-list-section-header{
90
+ display: none !important;
91
+ }
92
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper .options-list .single-wallet.sidebar.selected-wallet{
93
+ background-color: inherit;
94
+ }
95
+ .nws-modal-wrapper .nws-modal .wallet-options-wrapper .options-list .single-wallet.sidebar.selected-wallet:hover{
96
+ background-color: #2d3860;
97
+ }
98
+ `
99
+ // 一般可以保留样式,不需要卸载时删除
100
+ }, [])
101
+
102
+
103
+
104
+ return (
105
+ <WalletSelectorProvider config={config}>
106
+ {children}
107
+ </WalletSelectorProvider>
108
+ )
109
+ }
110
+
@@ -3,6 +3,7 @@ import { useConfig as useBigmiConfig } from '@bigmi/react'
3
3
  import type { SDKProvider } from '@openocean.finance/widget-sdk'
4
4
  import {
5
5
  ChainType,
6
+ Near,
6
7
  EVM,
7
8
  Solana,
8
9
  UTXO,
@@ -22,6 +23,7 @@ export const SDKProviders = () => {
22
23
  const { sdkConfig } = useWidgetConfig()
23
24
  const { wallet } = useWallet()
24
25
  const wagmiConfig = useWagmiConfig()
26
+ const nearProvider = Near()
25
27
  const bigmiConfig = useBigmiConfig()
26
28
 
27
29
  useEffect(() => {
@@ -63,6 +65,9 @@ export const SDKProviders = () => {
63
65
  })
64
66
  )
65
67
  }
68
+ // Always register a Near (NVM) provider so balances can be queried for NEAR.
69
+ providers.push(nearProvider)
70
+
66
71
  if (sdkConfig?.providers?.length) {
67
72
  providers.push(...sdkConfig.providers)
68
73
  }
@@ -11,7 +11,7 @@ interface ExternalWalletProvider {
11
11
  internalChainTypes: ChainType[]
12
12
  }
13
13
 
14
- const internalChainTypes = [ChainType.EVM, ChainType.SVM, ChainType.UTXO]
14
+ const internalChainTypes = [ChainType.EVM, ChainType.SVM, ChainType.UTXO, ChainType.NVM]
15
15
 
16
16
  export function useExternalWalletProvider(): ExternalWalletProvider {
17
17
  const { walletConfig } = useWidgetConfig()
@@ -261,6 +261,8 @@ interface ExecuteRouteOptions {
261
261
  onDisconnect?: (account: Account) => Promise<void>
262
262
  onOpenWalletMenu?: () => void
263
263
  solanaWallet?: any
264
+ // Near wallet-selector 实例从 React 层通过 options 传入,避免在此文件中直接调用 Hook
265
+ nearWallet?: any
264
266
  }
265
267
 
266
268
  interface ExtendedOpenOceanStep extends OpenOceanStep {
@@ -351,6 +353,7 @@ async function executeSolanaSwap(
351
353
  const signedTx = await bridgeExecuteSwap({
352
354
  quoteData: quoteData,
353
355
  walletClient: adaptedWallet,
356
+ nearWallet: options.nearWallet,
354
357
  })
355
358
  if (!signedTx) {
356
359
  throw new Error('Failed to sign transaction')
@@ -606,6 +609,7 @@ async function executeEvmSwap(
606
609
  const result = await bridgeExecuteSwap({
607
610
  quoteData: quoteData,
608
611
  walletClient: walletClient,
612
+ nearWallet: options.nearWallet,
609
613
  })
610
614
  hash = result?.sourceTxHash || ''
611
615
  } else {
@@ -1005,6 +1009,7 @@ async function executeBitcoinSwap(
1005
1009
  const signedTx = await bridgeExecuteSwap({
1006
1010
  quoteData: quoteData,
1007
1011
  walletClient: adaptedWallet,
1012
+ nearWallet: options.nearWallet,
1008
1013
  });
1009
1014
 
1010
1015
  if (!signedTx) {
@@ -1043,6 +1048,133 @@ async function executeBitcoinSwap(
1043
1048
  throw error
1044
1049
  }
1045
1050
  }
1051
+
1052
+ // Execute Near transaction via Near Intents adapter
1053
+ async function executeNearSwap(
1054
+ step: ExtendedOpenOceanStep,
1055
+ options: ExecuteRouteOptions,
1056
+ process: Process,
1057
+ route: ExtendedRoute
1058
+ ): Promise<void> {
1059
+ try {
1060
+ const { type, transactionRequest } = step || {}
1061
+
1062
+ if ((type as any) === 'bridge') {
1063
+
1064
+ const { quoteData } = step || {}
1065
+
1066
+ if (!quoteData) {
1067
+ throw new Error('Missing Near quote data.')
1068
+ }
1069
+
1070
+ // NearIntentsAdapter 的 Near 分支只依赖 nearWallet,不依赖 walletClient,
1071
+ // 这里传一个占位对象即可。
1072
+ const dummyWalletClient: any = {}
1073
+
1074
+ const result = await bridgeExecuteSwap({
1075
+ quoteData,
1076
+ walletClient: dummyWalletClient,
1077
+ nearWallet: options.nearWallet,
1078
+ })
1079
+
1080
+ if (!result) {
1081
+ throw new Error('Failed to execute Near swap.')
1082
+ }
1083
+
1084
+ const hash = result.sourceTxHash
1085
+
1086
+ process.status = 'DONE'
1087
+ process.doneAt = Date.now()
1088
+ process.txHash = hash
1089
+ process.message = 'Transaction confirmed'
1090
+ step.execution!.status = 'DONE'
1091
+ options.updateRouteHook?.(route)
1092
+ } else {
1093
+ let transactions = JSON.parse(Buffer.from(transactionRequest.data, 'base64').toString())
1094
+ const wallet = options.nearWallet
1095
+ const txs = transactions.map((t: any, i: number) => {
1096
+ return {
1097
+ receiverId: t.receiverId,
1098
+ // nonceOffset: i + 1,
1099
+ signerId: wallet.signedAccountId,
1100
+ actions: t.functionCalls.map((fc: any) => {
1101
+ if (fc.deposit) {
1102
+ const depositNum = typeof fc.deposit === 'string' ? parseFloat(fc.deposit) : fc.deposit
1103
+ if (depositNum > 0) {
1104
+ const depositStr = depositNum.toFixed(24)
1105
+ fc.deposit = depositStr.replace('.', '').replace(/^0+/, '') || '0'
1106
+ } else {
1107
+ fc.deposit = '0'
1108
+ }
1109
+ }
1110
+ return {
1111
+ type: 'FunctionCall',
1112
+ params: {
1113
+ methodName: fc.methodName,
1114
+ args: {
1115
+ ...fc.args,
1116
+ },
1117
+ gas: fc.gas,
1118
+ deposit: fc.deposit,
1119
+ },
1120
+ }
1121
+ })
1122
+ };
1123
+ })
1124
+ const txResult = await wallet.signAndSendTransactions({
1125
+ transactions: txs
1126
+ });
1127
+
1128
+ let transaction: any = { hash: "" };
1129
+ if (txResult && txResult.length === 1) {
1130
+ transaction = txResult[txResult.length - 1].transaction || {};
1131
+ } else if (txResult && txResult.length > 1) {
1132
+ transaction = txResult.filter((item: any) => {
1133
+ const { actions = [] } = item && item.transaction || {};
1134
+ const _actions = actions.filter((fc: any) => {
1135
+ const { FunctionCall = {} } = fc;
1136
+ const { method_name } = FunctionCall;
1137
+ return method_name === 'ft_transfer_call';
1138
+ });
1139
+ return _actions && _actions.length > 0;
1140
+ });
1141
+ if (transaction && transaction.length) {
1142
+ transaction = transaction[0].transaction;
1143
+ } else {
1144
+ transaction = txResult[txResult.length - 1].transaction || {};
1145
+ }
1146
+ }
1147
+ console.log('signAndSendTransactions', transaction);
1148
+ const { hash } = transaction;
1149
+ process.status = 'DONE'
1150
+ process.doneAt = Date.now()
1151
+ process.txHash = hash
1152
+ process.message = 'Transaction confirmed'
1153
+ step.execution!.status = 'DONE'
1154
+ options.updateRouteHook?.(route)
1155
+ }
1156
+ } catch (error) {
1157
+ console.error('Near swap execution failed:', error)
1158
+ process.status = 'FAILED'
1159
+ process.error =
1160
+ error instanceof Error || (error && (error as any)?.message)
1161
+ ? {
1162
+ code: 'EXECUTION_ERROR',
1163
+ message: error instanceof Error ? error.message : 'Unknown error occurred',
1164
+ htmlMessage:
1165
+ error instanceof Error ? error.message : 'Unknown error occurred',
1166
+ }
1167
+ : {
1168
+ code: 'UNKNOWN_ERROR',
1169
+ message: 'Unknown error occurred',
1170
+ htmlMessage: 'Unknown error occurred',
1171
+ }
1172
+ step.execution!.status = 'FAILED'
1173
+ options.updateRouteHook?.(route)
1174
+ throw error
1175
+ }
1176
+
1177
+ }
1046
1178
  // Execute transaction
1047
1179
  async function executeSwap(
1048
1180
  route: ExtendedRoute,
@@ -1081,13 +1213,14 @@ async function executeSwap(
1081
1213
  }
1082
1214
  throw new Error('Please connect wallet first')
1083
1215
  }
1084
-
1085
1216
  // Execute different transaction logic based on chain type
1086
1217
  const currentStep = route.steps[0]
1087
1218
  if (currentStep.action?.fromChainId === 1151111081099710) {
1088
1219
  await executeSolanaSwap(currentStep, options, process, updatedRoute)
1089
1220
  } else if (currentStep.action?.fromChainId === 20000000000001) {
1090
1221
  await executeBitcoinSwap(currentStep, options, process, updatedRoute)
1222
+ } else if (currentStep.action?.fromChainId === 20000000000006) {
1223
+ await executeNearSwap(currentStep, options, process, updatedRoute)
1091
1224
  } else {
1092
1225
  await executeEvmSwap(currentStep, options, process, updatedRoute)
1093
1226
  }
@@ -57,15 +57,68 @@ export class OpenOceanService {
57
57
  disabledDexIds?: string
58
58
  enabledDexIds?: string
59
59
  referrer?: string
60
+ account?: string
61
+ inTokenDecimals?: number
62
+ outTokenDecimals?: number
60
63
  }) {
64
+ const isNearChain = params.chain === '20000000000006' || params.chain === 'near'
65
+ // Near 链使用特殊的 API 端点和参数格式
66
+ if (isNearChain) {
67
+ const nearApiUrl = 'https://ethapi.openocean.finance/v1/near'
68
+
69
+ // 计算 amountAll(格式化后的金额,用于显示)
70
+ let amountAll = ''
71
+ if (params.inTokenDecimals !== undefined) {
72
+ const amountBigInt = BigInt(params.amount)
73
+ const decimals = BigInt(10 ** params.inTokenDecimals)
74
+ const wholePart = amountBigInt / decimals
75
+ const fractionalPart = amountBigInt % decimals
76
+ if (fractionalPart === 0n) {
77
+ amountAll = wholePart.toString()
78
+ } else {
79
+ const fractionalStr = fractionalPart.toString().padStart(params.inTokenDecimals, '0')
80
+ // 移除尾部的 0,但保留至少一位小数
81
+ const trimmedFractional = fractionalStr.replace(/0+$/, '') || '0'
82
+ amountAll = `${wholePart}.${trimmedFractional}`
83
+ }
84
+ }
85
+
86
+ // 转换 slippage:从 0.01 (1%) 转为 100 (百分比格式)
87
+ const slippagePercent = params.slippage
88
+ ? Math.floor(Number(params.slippage) * 100).toString()
89
+ : '100' // 默认 1%
90
+
91
+ const queryParams = new URLSearchParams({
92
+ quoteType: 'swap',
93
+ inTokenSymbol: params.inTokenSymbol,
94
+ inTokenAddress: params.inTokenAddress,
95
+ outTokenSymbol: params.outTokenSymbol,
96
+ outTokenAddress: params.outTokenAddress,
97
+ amount: params.amount,
98
+ ...(amountAll && { amountAll }),
99
+ slippage: slippagePercent,
100
+ gasPrice: params.gasPrice || '5000000000',
101
+ referrer: params.referrer || '0x3487ef9f9b36547e43268b8f0e2349a226c70b53',
102
+ disabledDexIds: params.disabledDexIds || '',
103
+ disableRfq: '', // Near API 特有参数
104
+ ...(params.account && { account: params.account }),
105
+ })
106
+ const response = await fetch(`${nearApiUrl}/quote?${queryParams.toString()}`)
107
+ return {
108
+ data: await response.json(),
109
+ }
110
+ }
111
+
112
+ // 其他链使用原有逻辑
61
113
  const apiUrl = this.getApiUrl(params.chain)
62
114
  const slippage = (Number(params.slippage || 0.01)).toString();
115
+
63
116
  const queryParams = new URLSearchParams({
64
117
  quoteType: 'quote',
65
118
  inTokenSymbol: params.inTokenSymbol,
66
- inTokenAddress: params.inTokenAddress,
119
+ inTokenAddress: this.getSolanaAddress(params.chain, params.inTokenAddress),
67
120
  outTokenSymbol: params.outTokenSymbol,
68
- outTokenAddress: params.outTokenAddress,
121
+ outTokenAddress: this.getSolanaAddress(params.chain, params.outTokenAddress),
69
122
  amountDecimals: params.amount,
70
123
  slippage,
71
124
  gasPriceDecimals: params.gasPrice || '',
@@ -73,7 +126,6 @@ export class OpenOceanService {
73
126
  enabledDexIds: params.enabledDexIds || '',
74
127
  referrer: params.referrer || '0x3487ef9f9b36547e43268b8f0e2349a226c70b53',
75
128
  })
76
-
77
129
  const response = await fetch(`${apiUrl}/quote?${queryParams.toString()}`)
78
130
  return response.json()
79
131
  }
@@ -93,7 +145,66 @@ export class OpenOceanService {
93
145
  referrer?: string,
94
146
  referrerFee?: string,
95
147
  referrerFeeShare?: string
148
+ inTokenDecimals?: number
96
149
  }) {
150
+ const isNearChain = params.chain === '20000000000006' || params.chain === 'near'
151
+
152
+ // Near 链使用特殊的 API 端点和参数格式
153
+ if (isNearChain) {
154
+ const nearApiUrl = 'https://ethapi.openocean.finance/v1/near'
155
+
156
+ // 计算 amountAll(格式化后的金额,用于显示)
157
+ let amountAll = ''
158
+ if (params.inTokenDecimals !== undefined) {
159
+ const amountBigInt = BigInt(params.amount)
160
+ const decimals = BigInt(10 ** params.inTokenDecimals)
161
+ const wholePart = amountBigInt / decimals
162
+ const fractionalPart = amountBigInt % decimals
163
+ if (fractionalPart === 0n) {
164
+ amountAll = wholePart.toString()
165
+ } else {
166
+ const fractionalStr = fractionalPart.toString().padStart(params.inTokenDecimals, '0')
167
+ // 移除尾部的 0,但保留至少一位小数
168
+ const trimmedFractional = fractionalStr.replace(/0+$/, '') || '0'
169
+ amountAll = `${wholePart}.${trimmedFractional}`
170
+ }
171
+ }
172
+
173
+ // 转换 slippage:从 0.01 (1%) 转为 100 (百分比格式)
174
+ const slippagePercent = params.slippage
175
+ ? Math.floor(Number(params.slippage) * 100).toString()
176
+ : '100' // 默认 1%
177
+
178
+ const queryParams = new URLSearchParams({
179
+ quoteType: 'swap',
180
+ inTokenSymbol: params.inTokenSymbol,
181
+ inTokenAddress: params.inTokenAddress,
182
+ outTokenSymbol: params.outTokenSymbol,
183
+ outTokenAddress: params.outTokenAddress,
184
+ amount: params.amount,
185
+ ...(amountAll && { amountAll }),
186
+ gasPrice: params.gasPrice || '5000000000',
187
+ disabledDexIds: params.disabledDexIds || '',
188
+ slippage: slippagePercent,
189
+ account: params.account,
190
+ referrer: params.referrer || '0x3487ef9f9b36547e43268b8f0e2349a226c70b53',
191
+ flags: '0', // Near API 特有参数
192
+ disableRfq: '', // Near API 特有参数
193
+ })
194
+
195
+ const response = await fetch(`${nearApiUrl}/swap-quote?${queryParams.toString()}`)
196
+ const data = await response.json()
197
+ return {
198
+
199
+ data: {
200
+ ...data,
201
+ data: data.transaction,
202
+ price_impact: 0
203
+ }
204
+ }
205
+ }
206
+
207
+ // 其他链使用原有逻辑
97
208
  const apiUrl = this.getApiUrl(params.chain)
98
209
  const slippage = (Number(params.slippage || 0.01)).toString();
99
210
  const referrer: any = {
@@ -176,7 +287,7 @@ export class OpenOceanService {
176
287
  }
177
288
 
178
289
  static async getGasPrice(chain: string) {
179
- if (!chain || chain === '1151111081099710' || chain === '20000000000001') {
290
+ if (!chain || chain === '1151111081099710' || chain === '20000000000001' || chain === '20000000000006') {
180
291
  return {
181
292
  data: {
182
293
  gasPrice: '1000000000000000000',
@@ -186,6 +297,13 @@ export class OpenOceanService {
186
297
  // const apiUrl = this.getApiUrl(chain)
187
298
  const response = await fetch(`${this.API_V4_URL}/${chain}/gasPrice`)
188
299
  const { data } = await response.json()
300
+ if (chain == '1') {
301
+ return {
302
+ standard: data?.standard?.maxFeePerGas || '101021713',
303
+ instant: data?.instant?.maxFeePerGas || '101021713',
304
+ fast: data?.fast?.maxFeePerGas || '101021713',
305
+ }
306
+ }
189
307
  return data
190
308
  }
191
309
 
@@ -255,9 +373,19 @@ export class OpenOceanService {
255
373
  let url = `${this.API_V3_URL}/solana/getRpc`
256
374
  const response = await fetch(url)
257
375
  const data = await response.json()
258
- let rpcUrl = data.data?.openapi_v1?.[0] || ''
259
- this.solanaRpcUrl = rpcUrl
260
- return rpcUrl
376
+ if (data.data?.openapi_v1) {
377
+ let rpcUrl = data.data?.openapi_v1?.[0] || ''
378
+ this.solanaRpcUrl = rpcUrl
379
+ }
380
+ if (data.data?.openapi_v2) {
381
+ let rpcUrl = data.data?.openapi_v2?.[0] || ''
382
+ this.solanaRpcUrl = rpcUrl
383
+ }
384
+ if (data.data?.openapi_v3) {
385
+ let rpcUrl = data.data?.openapi_v3?.[0] || ''
386
+ this.solanaRpcUrl = rpcUrl
387
+ }
388
+ return this.solanaRpcUrl
261
389
  }
262
390
  }
263
391