@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.
- package/dist/esm/AppProvider.js +2 -1
- package/dist/esm/AppProvider.js.map +1 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +21 -0
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TransactionDetails.js +6 -0
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/cross/adapters/NearIntentsAdapter.js +94 -25
- package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -1
- package/dist/esm/cross/constants/index.d.ts +1 -1
- package/dist/esm/cross/constants/index.js +1 -0
- package/dist/esm/cross/constants/index.js.map +1 -1
- package/dist/esm/cross/crossChainQuote.d.ts +3 -2
- package/dist/esm/cross/crossChainQuote.js +2 -2
- package/dist/esm/cross/crossChainQuote.js.map +1 -1
- package/dist/esm/hooks/useGasPrice.js +2 -2
- package/dist/esm/hooks/useGasPrice.js.map +1 -1
- package/dist/esm/hooks/useGasRecommendation.d.ts +1 -1
- package/dist/esm/hooks/useGasRecommendation.js +7 -1
- package/dist/esm/hooks/useGasRecommendation.js.map +1 -1
- package/dist/esm/hooks/useRouteExecution.js +5 -0
- package/dist/esm/hooks/useRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRoutes.js +1 -1
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useToken.js.map +1 -1
- package/dist/esm/providers/WalletProvider/NearProvider.d.ts +3 -0
- package/dist/esm/providers/WalletProvider/NearProvider.js +95 -0
- package/dist/esm/providers/WalletProvider/NearProvider.js.map +1 -0
- package/dist/esm/providers/WalletProvider/SDKProviders.js +4 -1
- package/dist/esm/providers/WalletProvider/SDKProviders.js.map +1 -1
- package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +1 -1
- package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -1
- package/dist/esm/services/ExecuteRoute.d.ts +1 -0
- package/dist/esm/services/ExecuteRoute.js +121 -0
- package/dist/esm/services/ExecuteRoute.js.map +1 -1
- package/dist/esm/services/OpenOceanService.d.ts +5 -1
- package/dist/esm/services/OpenOceanService.js +121 -6
- package/dist/esm/services/OpenOceanService.js.map +1 -1
- package/package.json +9 -5
- package/src/AppProvider.tsx +8 -5
- package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +20 -0
- package/src/components/TokenList/VirtualizedTokenList.tsx +1 -1
- package/src/components/TransactionDetails.tsx +17 -11
- package/src/config/version.ts +1 -1
- package/src/cross/adapters/NearIntentsAdapter.ts +100 -28
- package/src/cross/constants/index.ts +1 -0
- package/src/cross/crossChainQuote.ts +6 -2
- package/src/hooks/useGasPrice.ts +2 -2
- package/src/hooks/useGasRecommendation.ts +8 -1
- package/src/hooks/useRouteExecution.ts +5 -0
- package/src/hooks/useRoutes.ts +1 -1
- package/src/hooks/useToken.ts +0 -1
- package/src/providers/WalletProvider/NearProvider.tsx +110 -0
- package/src/providers/WalletProvider/SDKProviders.tsx +5 -0
- package/src/providers/WalletProvider/useExternalWalletProvider.ts +1 -1
- package/src/services/ExecuteRoute.ts +134 -1
- 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
|
|
582
|
-
|
|
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:
|
|
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: {
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
type: '
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
719
|
+
sourceTxHash: hash,
|
|
648
720
|
})
|
|
649
721
|
})
|
|
650
722
|
}
|
|
@@ -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
|
}
|
package/src/hooks/useGasPrice.ts
CHANGED
|
@@ -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 || '
|
|
16
|
+
const _gasPrice = data?.[gasKey[gasPrice || 'normal']]
|
|
17
17
|
return {
|
|
18
|
-
gasPrice: _gasPrice
|
|
18
|
+
gasPrice: _gasPrice || _gasPrice?.maxFeePerGas || data?.gasPrice || 50000000,
|
|
19
19
|
isLoading,
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
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: () => {
|
package/src/hooks/useRoutes.ts
CHANGED
|
@@ -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'
|
package/src/hooks/useToken.ts
CHANGED
|
@@ -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
|
}
|