@openocean.finance/widget 1.0.51 → 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 (60) 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/constants/index.d.ts +1 -1
  14. package/dist/esm/cross/constants/index.js +1 -0
  15. package/dist/esm/cross/constants/index.js.map +1 -1
  16. package/dist/esm/cross/crossChainQuote.d.ts +3 -2
  17. package/dist/esm/cross/crossChainQuote.js +2 -2
  18. package/dist/esm/cross/crossChainQuote.js.map +1 -1
  19. package/dist/esm/hooks/useGasPrice.js +2 -2
  20. package/dist/esm/hooks/useGasPrice.js.map +1 -1
  21. package/dist/esm/hooks/useGasRecommendation.d.ts +1 -1
  22. package/dist/esm/hooks/useGasRecommendation.js +7 -1
  23. package/dist/esm/hooks/useGasRecommendation.js.map +1 -1
  24. package/dist/esm/hooks/useRouteExecution.js +5 -0
  25. package/dist/esm/hooks/useRouteExecution.js.map +1 -1
  26. package/dist/esm/hooks/useRoutes.js +1 -1
  27. package/dist/esm/hooks/useRoutes.js.map +1 -1
  28. package/dist/esm/hooks/useToken.js.map +1 -1
  29. package/dist/esm/providers/WalletProvider/NearProvider.d.ts +3 -0
  30. package/dist/esm/providers/WalletProvider/NearProvider.js +95 -0
  31. package/dist/esm/providers/WalletProvider/NearProvider.js.map +1 -0
  32. package/dist/esm/providers/WalletProvider/SDKProviders.js +4 -1
  33. package/dist/esm/providers/WalletProvider/SDKProviders.js.map +1 -1
  34. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +1 -1
  35. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
  36. package/dist/esm/services/ExecuteRoute.d.ts +1 -0
  37. package/dist/esm/services/ExecuteRoute.js +121 -0
  38. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  39. package/dist/esm/services/OpenOceanService.d.ts +5 -1
  40. package/dist/esm/services/OpenOceanService.js +121 -6
  41. package/dist/esm/services/OpenOceanService.js.map +1 -1
  42. package/package.json +9 -5
  43. package/src/AppProvider.tsx +8 -5
  44. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +20 -0
  45. package/src/components/TokenList/VirtualizedTokenList.tsx +1 -1
  46. package/src/components/TransactionDetails.tsx +17 -11
  47. package/src/config/version.ts +1 -1
  48. package/src/cross/adapters/NearIntentsAdapter.ts +100 -28
  49. package/src/cross/constants/index.ts +1 -0
  50. package/src/cross/crossChainQuote.ts +6 -2
  51. package/src/hooks/useGasPrice.ts +2 -2
  52. package/src/hooks/useGasRecommendation.ts +8 -1
  53. package/src/hooks/useRouteExecution.ts +5 -0
  54. package/src/hooks/useRoutes.ts +1 -1
  55. package/src/hooks/useToken.ts +0 -1
  56. package/src/providers/WalletProvider/NearProvider.tsx +110 -0
  57. package/src/providers/WalletProvider/SDKProviders.tsx +5 -0
  58. package/src/providers/WalletProvider/useExternalWalletProvider.ts +1 -1
  59. package/src/services/ExecuteRoute.ts +134 -1
  60. package/src/services/OpenOceanService.ts +135 -7
@@ -209,6 +209,7 @@ export class NearIntentsAdapter extends BaseSwapAdapter {
209
209
  deadline.setSeconds(deadline.getSeconds() + (params.fromChain === NonEvmChain.Bitcoin ? 60 * 60 : 60 * 20))
210
210
  if (this.nearTokens.length === 0) {
211
211
  await this.getAllSupportedTokens()
212
+ await new Promise(resolve => setTimeout(resolve, 3000))
212
213
  }
213
214
 
214
215
  let fromAssetId: any = ''
@@ -576,75 +577,146 @@ export class NearIntentsAdapter extends BaseSwapAdapter {
576
577
  reject('Not connected')
577
578
  return
578
579
  }
579
- const isNative = (quote.quoteParams.fromToken as any).assetId === 'near'
580
580
 
581
- const transactions: any = []
582
- if (!isNative)
581
+ const fromToken = quote.quoteParams.fromToken as any
582
+ const isNative = fromToken.address === 'near.near'
583
+ const rawAmount = quote.quoteParams.amount || '0'
584
+ const amount = String(rawAmount) // yoctoNEAR 字符串
585
+
586
+ const transactions: {
587
+ signerId: string
588
+ receiverId: string
589
+ actions: any[]
590
+ }[] = []
591
+
592
+ // wNEAR 合约地址(标准 wrap.near)
593
+ const WRAP_CONTRACT_ID = 'wrap.near'
594
+
595
+ const tokenContract = fromToken.address as string | undefined
596
+
597
+ if (isNative) {
598
+ // 原生 NEAR:先在 wNEAR 合约上给桥地址做 storage_deposit,再 near_deposit 包成 wNEAR,然后 ft_transfer 给桥地址
583
599
  transactions.push({
584
600
  signerId: nearWallet.signedAccountId,
585
- receiverId: (quote.quoteParams.fromToken as any).contractAddress,
601
+ receiverId: WRAP_CONTRACT_ID,
586
602
  actions: [
587
603
  {
604
+ // 1) storage_deposit,确保桥地址在 wNEAR 上已注册
588
605
  type: 'FunctionCall',
589
606
  params: {
590
607
  methodName: 'storage_deposit',
591
- args: { account_id: depositAddress, registration_only: true },
608
+ args: {
609
+ account_id: depositAddress,
610
+ registration_only: true,
611
+ },
592
612
  gas: '30000000000000',
593
613
  deposit: '1250000000000000000000', // 0.00125 NEAR
594
614
  },
595
615
  },
616
+ {
617
+ // 2) near_deposit:把原生 NEAR 包成 wNEAR
618
+ type: 'FunctionCall',
619
+ params: {
620
+ methodName: 'near_deposit',
621
+ args: {},
622
+ gas: '30000000000000',
623
+ deposit: amount, // 使用原始 NEAR 数量(yoctoNEAR)
624
+ },
625
+ },
626
+ {
627
+ // 3) ft_transfer:将 wNEAR 发送到桥地址
628
+ type: 'FunctionCall',
629
+ params: {
630
+ methodName: 'ft_transfer',
631
+ args: {
632
+ receiver_id: depositAddress,
633
+ amount,
634
+ },
635
+ gas: '30000000000000',
636
+ deposit: '1', // NEP-141 规范要求 1 yoctoNEAR
637
+ },
638
+ },
596
639
  ],
597
640
  })
598
-
599
- transactions.push({
600
- signerId: nearWallet.signedAccountId,
601
- receiverId: isNative ? depositAddress : (quote.quoteParams.fromToken as any).contractAddress,
602
- actions: [
603
- isNative
604
- ? {
605
- type: 'Transfer',
641
+ } else if (tokenContract) {
642
+ // 非原生 NEP-141 token:先在 token 合约上给桥地址做 storage_deposit,再 ft_transfer
643
+ transactions.push({
644
+ signerId: nearWallet.signedAccountId,
645
+ receiverId: tokenContract,
646
+ actions: [
647
+ {
648
+ type: 'FunctionCall',
606
649
  params: {
607
- deposit: quote.quoteParams.amount,
650
+ methodName: 'storage_deposit',
651
+ args: {
652
+ account_id: depositAddress,
653
+ registration_only: true,
654
+ },
655
+ gas: '30000000000000',
656
+ deposit: '1250000000000000000000', // 0.00125 NEAR
608
657
  },
609
- }
610
- : {
658
+ },
659
+ {
611
660
  type: 'FunctionCall',
612
661
  params: {
613
662
  methodName: 'ft_transfer',
614
663
  args: {
615
664
  receiver_id: depositAddress,
616
- amount: quote.quoteParams.amount,
665
+ amount,
617
666
  },
618
667
  gas: '30000000000000',
619
668
  deposit: '1',
620
669
  },
621
670
  },
622
- ],
623
- })
671
+ ],
672
+ })
673
+ } else {
674
+ reject('Invalid NEAR token contract')
675
+ return
676
+ }
624
677
 
625
- // My near wallet is redirect to wallet website -> need store to process later
626
- if (nearWallet?.wallet?.id === 'my-near-wallet')
678
+ // MyNearWallet 会跳转到网页,需要在本地记录一次
679
+ if (nearWallet?.wallet?.id === 'my-near-wallet') {
627
680
  localStorage.setItem(
628
681
  'cross-chain-swap-my-near-wallet-tx',
629
682
  JSON.stringify({
630
683
  ...params,
631
684
  sourceTxHash: depositAddress,
632
- }),
685
+ })
633
686
  )
687
+ }
634
688
 
635
- await nearWallet
636
- .signAndSendTransactions({
637
- transactions,
638
- })
639
- .catch(e => {
689
+ const txResult = await nearWallet
690
+ .signAndSendTransactions({ transactions })
691
+ .catch((e: any) => {
640
692
  console.log('NearIntents signAndSendTransactions failed', e)
641
693
  if (nearWallet?.wallet?.id === 'my-near-wallet') reject()
642
694
  else reject(e)
643
695
  })
696
+ let transaction: any = { hash: "" };
697
+ if (txResult && txResult.length === 1) {
698
+ transaction = txResult[txResult.length - 1].transaction || {};
699
+ } else if (txResult && txResult.length > 1) {
700
+ transaction = txResult.filter((item: any) => {
701
+ const { actions = [] } = item && item.transaction || {};
702
+ const _actions = actions.filter((fc: any) => {
703
+ const { FunctionCall = {} } = fc;
704
+ const { method_name } = FunctionCall;
705
+ return method_name === 'ft_transfer_call';
706
+ });
707
+ return _actions && _actions.length > 0;
708
+ });
709
+ if (transaction && transaction.length) {
710
+ transaction = transaction[0].transaction;
711
+ } else {
712
+ transaction = txResult[txResult.length - 1].transaction || {};
713
+ }
714
+ }
715
+ const { hash } = transaction;
644
716
 
645
717
  resolve({
646
718
  ...params,
647
- sourceTxHash: depositAddress,
719
+ sourceTxHash: hash,
648
720
  })
649
721
  })
650
722
  }
@@ -199,6 +199,7 @@ export const MAINNET_NETWORKS = [
199
199
  ChainId.MONAD,
200
200
  ChainId.FLR,
201
201
  ChainId.CRO,
202
+ ChainId.RSK,
202
203
  ] as const
203
204
 
204
205
  export interface Currency {
@@ -213,19 +213,23 @@ export async function getCrossChainQuote({
213
213
  export const bridgeExecuteSwap = async ({
214
214
  quoteData,
215
215
  walletClient,
216
+ nearWallet,
216
217
  }: {
217
218
  quoteData: any
218
219
  walletClient: WalletClient
220
+ // Near Intents adapter 需要的 Near wallet-selector 实例,其它适配器会忽略
221
+ nearWallet?: any
219
222
  }) => {
220
223
  const adapterName = quoteData.quoteAdapterKey
221
224
  const adapter = CrossChainSwapFactory.getAdapterByName(adapterName)
222
225
  if (adapter) {
223
- const result = await adapter?.executeSwap(
226
+ const result = await (adapter as any)?.executeSwap(
224
227
  {
225
228
  adapter,
226
229
  quote: quoteData.quoteRawData as any,
227
230
  },
228
- walletClient
231
+ walletClient,
232
+ nearWallet
229
233
  )
230
234
  return result
231
235
  }
@@ -13,9 +13,9 @@ export const useGasPrice = (chainName: string) => {
13
13
  queryKey: ['gasPrice'],
14
14
  queryFn: () => OpenOceanService.getGasPrice(chainName.toLowerCase()),
15
15
  })
16
- const _gasPrice = data?.[gasKey[gasPrice || 'Normal']]
16
+ const _gasPrice = data?.[gasKey[gasPrice || 'normal']]
17
17
  return {
18
- gasPrice: _gasPrice && _gasPrice.maxFeePerGas,
18
+ gasPrice: _gasPrice || _gasPrice?.maxFeePerGas || data?.gasPrice || 50000000,
19
19
  isLoading,
20
20
  }
21
21
  }
@@ -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
  }