@openocean.finance/widget 1.0.43 → 1.0.44

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 (145) hide show
  1. package/dist/esm/App.js.map +1 -1
  2. package/dist/esm/AppDrawer.style.d.ts +1 -1
  3. package/dist/esm/components/ActiveTransactions/ActiveTransactions.style.d.ts +2 -2
  4. package/dist/esm/components/AmountInput/AmountInput.style.d.ts +1 -1
  5. package/dist/esm/components/AmountInput/AmountInputAdornment.style.d.ts +1 -1
  6. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +37 -33
  7. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  8. package/dist/esm/components/Avatar/Avatar.style.d.ts +1 -1
  9. package/dist/esm/components/Avatar/SmallAvatar.d.ts +1 -1
  10. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +1 -1
  11. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
  12. package/dist/esm/components/ButtonTertiary.d.ts +1 -1
  13. package/dist/esm/components/Card/CardHeader.d.ts +1 -1
  14. package/dist/esm/components/Card/CardIconButton.d.ts +1 -1
  15. package/dist/esm/components/ContractComponent/NFT/NFT.style.d.ts +1 -1
  16. package/dist/esm/components/Header/Header.style.d.ts +2 -2
  17. package/dist/esm/components/Header/SettingsButton.style.d.ts +2 -2
  18. package/dist/esm/components/ListItem/ListItem.d.ts +1 -1
  19. package/dist/esm/components/Messages/WarningMessages.js +1 -0
  20. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  21. package/dist/esm/components/Messages/useMessageQueue.js +1 -1
  22. package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
  23. package/dist/esm/components/Search/SearchInput.style.d.ts +1 -1
  24. package/dist/esm/components/SelectTokenButton/SelectTokenButton.style.d.ts +1 -1
  25. package/dist/esm/components/SendToWallet/SendToWallet.style.d.ts +1 -1
  26. package/dist/esm/components/Skeleton/WidgetSkeleton.style.d.ts +2 -2
  27. package/dist/esm/components/StepActions/StepActions.style.d.ts +1 -1
  28. package/dist/esm/components/Tabs/Tabs.style.d.ts +2 -2
  29. package/dist/esm/components/TokenList/TokenList.style.d.ts +2 -2
  30. package/dist/esm/components/TransactionDetails.js +1 -1
  31. package/dist/esm/components/TransactionDetails.js.map +1 -1
  32. package/dist/esm/config/defaultChainIds.js +3 -0
  33. package/dist/esm/config/defaultChainIds.js.map +1 -1
  34. package/dist/esm/config/version.d.ts +1 -1
  35. package/dist/esm/config/version.js +1 -1
  36. package/dist/esm/cross/adapters/AcrossAdapter.js +21 -22
  37. package/dist/esm/cross/adapters/AcrossAdapter.js.map +1 -1
  38. package/dist/esm/cross/adapters/BaseSwapAdapter.d.ts +17 -9
  39. package/dist/esm/cross/adapters/BaseSwapAdapter.js +15 -4
  40. package/dist/esm/cross/adapters/BaseSwapAdapter.js.map +1 -1
  41. package/dist/esm/cross/adapters/DebridgeAdapter.js +8 -7
  42. package/dist/esm/cross/adapters/DebridgeAdapter.js.map +1 -1
  43. package/dist/esm/cross/adapters/LifiAdapter.js +7 -6
  44. package/dist/esm/cross/adapters/LifiAdapter.js.map +1 -1
  45. package/dist/esm/cross/adapters/MayanAdapter.js +1 -2
  46. package/dist/esm/cross/adapters/MayanAdapter.js.map +1 -1
  47. package/dist/esm/cross/adapters/NearIntentsAdapter.d.ts +13 -6
  48. package/dist/esm/cross/adapters/NearIntentsAdapter.js +252 -75
  49. package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -1
  50. package/dist/esm/cross/adapters/OptimexAdapter.js +2 -1
  51. package/dist/esm/cross/adapters/OptimexAdapter.js.map +1 -1
  52. package/dist/esm/cross/adapters/OrbiterAdapter.js +9 -8
  53. package/dist/esm/cross/adapters/OrbiterAdapter.js.map +1 -1
  54. package/dist/esm/cross/adapters/RelayAdapter.js +46 -15
  55. package/dist/esm/cross/adapters/RelayAdapter.js.map +1 -1
  56. package/dist/esm/cross/adapters/SymbiosisAdapter.js +13 -14
  57. package/dist/esm/cross/adapters/SymbiosisAdapter.js.map +1 -1
  58. package/dist/esm/cross/adapters/XYFinanceAdapter.js +13 -17
  59. package/dist/esm/cross/adapters/XYFinanceAdapter.js.map +1 -1
  60. package/dist/esm/cross/adapters/index.d.ts +2 -0
  61. package/dist/esm/cross/adapters/index.js +2 -1
  62. package/dist/esm/cross/adapters/index.js.map +1 -1
  63. package/dist/esm/cross/constants/index.d.ts +3 -1
  64. package/dist/esm/cross/constants/index.js +2 -0
  65. package/dist/esm/cross/constants/index.js.map +1 -1
  66. package/dist/esm/cross/crossChainQuote.d.ts +1 -1
  67. package/dist/esm/cross/crossChainQuote.js +22 -12
  68. package/dist/esm/cross/crossChainQuote.js.map +1 -1
  69. package/dist/esm/cross/factory.d.ts +3 -1
  70. package/dist/esm/cross/factory.js +10 -12
  71. package/dist/esm/cross/factory.js.map +1 -1
  72. package/dist/esm/cross/registry.d.ts +1 -1
  73. package/dist/esm/hooks/useAvailableChains.js +47 -40
  74. package/dist/esm/hooks/useAvailableChains.js.map +1 -1
  75. package/dist/esm/hooks/useChains.js.map +1 -1
  76. package/dist/esm/hooks/useRoutes.js +11 -3
  77. package/dist/esm/hooks/useRoutes.js.map +1 -1
  78. package/dist/esm/hooks/useTokenAddressBalance.js.map +1 -1
  79. package/dist/esm/hooks/useTokens.d.ts +1 -0
  80. package/dist/esm/hooks/useTokens.js +14 -0
  81. package/dist/esm/hooks/useTokens.js.map +1 -1
  82. package/dist/esm/index.js.map +1 -1
  83. package/dist/esm/pages/MainPage/MainWarningMessages.js.map +1 -1
  84. package/dist/esm/pages/SendToWallet/BookmarksPage.js +1 -1
  85. package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
  86. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +2 -2
  87. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
  88. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +2 -2
  89. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
  90. package/dist/esm/pages/SendToWallet/SendToWalletPage.style.d.ts +4 -4
  91. package/dist/esm/pages/SettingsPage/SettingsCard/SettingCard.style.d.ts +1 -1
  92. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  93. package/dist/esm/services/ExecuteRoute.js +218 -9
  94. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  95. package/dist/esm/services/OpenOceanService.js +30 -4
  96. package/dist/esm/services/OpenOceanService.js.map +1 -1
  97. package/dist/esm/stores/bookmarks/createBookmarkStore.js.map +1 -1
  98. package/dist/esm/utils/chainType.d.ts +1 -0
  99. package/dist/esm/utils/chainType.js +15 -0
  100. package/dist/esm/utils/chainType.js.map +1 -1
  101. package/dist/esm/utils/getPriceImpact.js +3 -3
  102. package/dist/esm/utils/getPriceImpact.js.map +1 -1
  103. package/package.json +18 -6
  104. package/src/App.tsx +0 -1
  105. package/src/components/AmountInput/AmountInputEndAdornment.tsx +39 -34
  106. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +3 -2
  107. package/src/components/Messages/WarningMessages.tsx +1 -0
  108. package/src/components/Messages/useMessageQueue.ts +1 -1
  109. package/src/components/TransactionDetails.tsx +4 -4
  110. package/src/config/defaultChainIds.ts +3 -0
  111. package/src/config/version.ts +1 -1
  112. package/src/cross/adapters/AcrossAdapter.ts +21 -22
  113. package/src/cross/adapters/BaseSwapAdapter.ts +24 -8
  114. package/src/cross/adapters/DebridgeAdapter.ts +11 -11
  115. package/src/cross/adapters/LifiAdapter.ts +11 -10
  116. package/src/cross/adapters/MayanAdapter.ts +1 -2
  117. package/src/cross/adapters/NearIntentsAdapter.ts +303 -129
  118. package/src/cross/adapters/OptimexAdapter.ts +12 -11
  119. package/src/cross/adapters/OrbiterAdapter.ts +17 -16
  120. package/src/cross/adapters/RelayAdapter.ts +50 -17
  121. package/src/cross/adapters/SymbiosisAdapter.ts +13 -14
  122. package/src/cross/adapters/XYFinanceAdapter.ts +15 -19
  123. package/src/cross/adapters/index.ts +2 -1
  124. package/src/cross/constants/index.ts +4 -0
  125. package/src/cross/crossChainQuote.ts +27 -21
  126. package/src/cross/factory.ts +12 -12
  127. package/src/cross/registry.ts +1 -1
  128. package/src/hooks/useAvailableChains.ts +50 -43
  129. package/src/hooks/useChains.ts +0 -1
  130. package/src/hooks/useExplorer.ts +6 -6
  131. package/src/hooks/useRoutes.ts +12 -4
  132. package/src/hooks/useTokenAddressBalance.ts +1 -1
  133. package/src/hooks/useTokens.ts +20 -5
  134. package/src/index.ts +0 -1
  135. package/src/pages/MainPage/MainWarningMessages.tsx +0 -1
  136. package/src/pages/SendToWallet/BookmarksPage.tsx +1 -1
  137. package/src/pages/SendToWallet/RecentWalletsPage.tsx +2 -2
  138. package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +2 -2
  139. package/src/providers/WidgetProvider/WidgetProvider.tsx +0 -1
  140. package/src/services/ExecuteRoute.ts +315 -64
  141. package/src/services/OpenOceanService.ts +31 -7
  142. package/src/stores/bookmarks/createBookmarkStore.ts +15 -15
  143. package/src/utils/chainType.ts +25 -1
  144. package/src/utils/getPriceImpact.ts +3 -3
  145. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -12,7 +12,16 @@ import { getPublicClient, getWalletClient } from 'wagmi/actions'
12
12
  import { bridgeExecuteSwap } from '../cross/crossChainQuote.js'
13
13
  import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
14
14
  import { sendAndConfirmSolanaTransaction } from './SendAndConfirmSolanaTransaction.js'
15
-
15
+ import { adaptBitcoinWallet } from '@relayprotocol/relay-bitcoin-wallet-adapter'
16
+ import * as bitcoin from 'bitcoinjs-lib';
17
+ type DynamicSignPsbtParams = {
18
+ allowedSighash: number[];
19
+ unsignedPsbtBase64: string;
20
+ signature: Array<{
21
+ address: string;
22
+ signingIndexes: number[];
23
+ }>;
24
+ };
16
25
  const OpenOceanABI = [
17
26
  {
18
27
  inputs: [
@@ -164,24 +173,24 @@ async function swapQuoteMEV(
164
173
  const inAmount = response?.inAmount
165
174
  const inTokenDecimals = response?.inToken?.decimals || 18
166
175
  const inTokenPrice = Number(response?.inToken?.priceUSD || 0)
167
- const amount = decimals2Amount(inAmount, inTokenDecimals) * inTokenPrice
176
+ const amount = decimals2Amount(inAmount as string, inTokenDecimals) * inTokenPrice
168
177
  if (amount < 1) {
169
178
  return response
170
179
  }
171
- const { publicClient } = options
180
+ const { publicClient } = options || {}
172
181
  const OPENOCEAN_CONTRACT = new ethers.Contract(
173
182
  '0x6352a56caadC4F1E25CD6c75970Fa768A3304e64',
174
183
  OpenOceanABI
175
184
  )
176
185
  if (
177
186
  ethers.hexlify(ethers.getBytes(response?.data || '0x').slice(0, 4)) !==
178
- OPENOCEAN_CONTRACT.interface.getFunction('swap').selector
187
+ OPENOCEAN_CONTRACT?.interface?.getFunction('swap')?.selector
179
188
  ) {
180
189
  return response
181
190
  }
182
191
  const oldCallData = OPENOCEAN_CONTRACT.interface.decodeFunctionData(
183
192
  'swap',
184
- response?.data
193
+ response?.data as any
185
194
  )
186
195
  const callData = [...oldCallData]
187
196
  callData[1] = [...oldCallData[1]]
@@ -291,44 +300,53 @@ async function executeSolanaSwap(
291
300
 
292
301
  const { transactionRequest, type, quoteData } = step || {}
293
302
  if ((type as any) === 'bridge') {
294
- const adaptedWallet: any = adaptSolanaWallet(
295
- options.account.address?.toString() ||
296
- '1nc1nerator11111111111111111111111111111111',
297
- 792703809, //chain id that Relay uses to identify solana
298
- connection,
299
- async (transaction) => {
300
- try {
301
- // Ensure transaction is properly formatted
302
- if (
303
- transaction instanceof VersionedTransaction ||
304
- transaction instanceof Transaction
305
- ) {
306
- const connector = options.account.connector as any
307
- if (typeof connector.signTransaction !== 'function') {
308
- throw new Error('Wallet does not support transaction signing')
309
- }
310
- // Sign transaction
311
- const signature = await connector.signTransaction(transaction)
312
- const serializedTransaction = signature.serialize({
313
- verifySignatures: false,
314
- requireAllSignatures: false,
315
- })
316
- const txid = await connection.sendRawTransaction(
317
- serializedTransaction,
318
- {
319
- skipPreflight: true,
320
- }
321
- )
322
- return { signature: txid }
303
+ // Define signAndSendTransaction function
304
+ const signAndSendTransaction = async (transaction: Transaction | VersionedTransaction) => {
305
+ try {
306
+ // Ensure transaction is properly formatted
307
+ if (
308
+ transaction instanceof VersionedTransaction ||
309
+ transaction instanceof Transaction
310
+ ) {
311
+ const connector = options.account.connector as any
312
+ if (typeof connector.signTransaction !== 'function') {
313
+ throw new Error('Wallet does not support transaction signing')
323
314
  }
324
315
 
325
- throw new Error('Invalid transaction type')
326
- } catch (error) {
327
- console.error('Transaction sending failed:', error)
328
- throw error
316
+ // Sign transaction
317
+ const signature = await connector.signTransaction(transaction)
318
+ const serializedTransaction = signature.serialize({
319
+ verifySignatures: false,
320
+ requireAllSignatures: false,
321
+ })
322
+ const txid = await connection.sendRawTransaction(
323
+ serializedTransaction,
324
+ {
325
+ skipPreflight: true,
326
+ }
327
+ )
328
+ return { signature: txid }
329
329
  }
330
+
331
+ throw new Error('Invalid transaction type')
332
+ } catch (error) {
333
+ console.error('Transaction sending failed:', error)
334
+ throw error
330
335
  }
336
+ }
337
+
338
+ const adaptedWallet: any = adaptSolanaWallet(
339
+ options.account.address?.toString() ||
340
+ '1nc1nerator11111111111111111111111111111111',
341
+ 792703809, //chain id that Relay uses to identify solana
342
+ connection,
343
+ signAndSendTransaction
331
344
  )
345
+
346
+ // Expose connection and sendTransaction method for adapters to use
347
+ adaptedWallet.connection = connection
348
+ adaptedWallet.sendTransaction = signAndSendTransaction
349
+
332
350
  const signedTx = await bridgeExecuteSwap({
333
351
  quoteData: quoteData,
334
352
  walletClient: adaptedWallet,
@@ -352,7 +370,7 @@ async function executeSolanaSwap(
352
370
  const txData: any = transactionRequest?.data || ''
353
371
  const dexId = transactionRequest?.type || 0
354
372
  if (step.action.fromChainId === step.action.toChainId) {
355
- if (dexId === 6 || dexId === 7 || dexId === 9) {
373
+ if (dexId === 6 || dexId === 7 || dexId === 9 || dexId === 10) {
356
374
  transaction = VersionedTransaction.deserialize(
357
375
  hexToUint8Array(txData)
358
376
  )
@@ -415,15 +433,15 @@ async function executeSolanaSwap(
415
433
  process.error =
416
434
  error instanceof Error
417
435
  ? {
418
- code: 'EXECUTION_ERROR',
419
- message: error.message,
420
- htmlMessage: error.message,
421
- }
436
+ code: 'EXECUTION_ERROR',
437
+ message: error.message,
438
+ htmlMessage: error.message,
439
+ }
422
440
  : {
423
- code: 'UNKNOWN_ERROR',
424
- message: 'Unknown error occurred',
425
- htmlMessage: 'Unknown error occurred',
426
- }
441
+ code: 'UNKNOWN_ERROR',
442
+ message: 'Unknown error occurred',
443
+ htmlMessage: 'Unknown error occurred',
444
+ }
427
445
  step.execution!.status = 'FAILED'
428
446
  options.updateRouteHook?.(route)
429
447
  throw error
@@ -439,7 +457,6 @@ async function executeEvmSwap(
439
457
  ): Promise<void> {
440
458
  try {
441
459
  let walletClient = await getWalletClient(options.wagmiConfig)
442
-
443
460
  // Check if wallet is connected
444
461
  if (!walletClient) {
445
462
  if (options.account?.connector && options.onDisconnect) {
@@ -460,8 +477,7 @@ async function executeEvmSwap(
460
477
  )
461
478
  const currentChainId = await walletClient.getChainId()
462
479
  const targetChainId = step.action.fromChainId
463
-
464
- if (currentChainId != targetChainId) {
480
+ if (currentChainId != targetChainId && targetChainId != 20000000000001) {
465
481
  try {
466
482
  // Try to switch to target chain
467
483
  await walletClient.switchChain({ id: targetChainId })
@@ -508,7 +524,7 @@ async function executeEvmSwap(
508
524
  '0x0000000000000000000000000000000000001010',
509
525
  ].indexOf(step.action.fromToken.address) === -1 &&
510
526
  step.estimate.approvalAddress !==
511
- '0x0000000000000000000000000000000000000000'
527
+ '0x0000000000000000000000000000000000000000'
512
528
  ) {
513
529
  let allowance = 0n
514
530
  try {
@@ -546,14 +562,14 @@ async function executeEvmSwap(
546
562
  BigInt(step.action.fromAmount) +
547
563
  BigInt(
548
564
  route?.prependedOperatingExpenseCost ||
549
- route?.data?.prependedOperatingExpenseCost ||
550
- '0'
565
+ route?.data?.prependedOperatingExpenseCost ||
566
+ '0'
551
567
  )
552
568
  if (allowance < BigInt(amount)) {
553
569
  const approvalAmount = options.infiniteApproval
554
570
  ? BigInt(
555
- '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
556
- )
571
+ '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
572
+ )
557
573
  : BigInt(amount)
558
574
 
559
575
  const hash = await walletClient.writeContract({
@@ -590,7 +606,7 @@ async function executeEvmSwap(
590
606
  quoteData: quoteData,
591
607
  walletClient: walletClient,
592
608
  })
593
- hash = result.sourceTxHash
609
+ hash = result?.sourceTxHash || ''
594
610
  } else {
595
611
  const txRequest = {
596
612
  chain: publicClient.chain,
@@ -672,21 +688,252 @@ async function executeEvmSwap(
672
688
  process.error =
673
689
  error instanceof Error
674
690
  ? {
675
- code: 'EXECUTION_ERROR',
676
- message: error.message,
677
- htmlMessage: error.message,
678
- }
691
+ code: 'EXECUTION_ERROR',
692
+ message: error.message,
693
+ htmlMessage: error.message,
694
+ }
679
695
  : {
680
- code: 'UNKNOWN_ERROR',
681
- message: 'Unknown error occurred',
682
- htmlMessage: 'Unknown error occurred',
683
- }
696
+ code: 'UNKNOWN_ERROR',
697
+ message: 'Unknown error occurred',
698
+ htmlMessage: 'Unknown error occurred',
699
+ }
684
700
  step.execution!.status = 'FAILED'
685
701
  options.updateRouteHook?.(route)
686
702
  throw error
687
703
  }
688
704
  }
705
+ function detectBtcWallet() {
706
+ const anyWindow = typeof window !== 'undefined' ? (window as any) : undefined
707
+ if (anyWindow.okxwallet?.bitcoin && anyWindow.okxwallet?.bitcoin?.signPsbt) return 'okx';
708
+ if (anyWindow.unisat && anyWindow.unisat?.signPsbt) return 'unisat';
709
+ if (anyWindow.BitcoinProvider && anyWindow.BitcoinProvider.request) return 'xverse';
710
+ return null;
711
+ }
712
+
713
+ // Get wallet type from connected account
714
+ function getConnectedBtcWallet(account: Account): string | null {
715
+ if (!account.connector) return null;
716
+
717
+ const connectorId = (account.connector as any).id?.toLowerCase() || '';
718
+ const connectorName = (account.connector as any).name?.toLowerCase() || '';
719
+
720
+ // Match by connector ID or name
721
+ if (connectorId.includes('okx') || connectorId.includes('com.okex.wallet')) {
722
+ return 'okx';
723
+ }
724
+ if (connectorId.includes('unisat')) {
725
+ return 'unisat';
726
+ }
727
+ if (connectorId.includes('xverse') || connectorId.includes('XverseProviders')) {
728
+ return 'xverse';
729
+ }
730
+ if (connectorId.includes('phantom') || connectorName.includes('phantom')) {
731
+ return 'phantom';
732
+ }
733
+ if (connectorId.includes('leather') || connectorName.includes('leather')) {
734
+ return 'leather';
735
+ }
736
+ if (connectorId.includes('onekey') || connectorName.includes('onekey')) {
737
+ return 'onekey';
738
+ }
739
+
740
+ // Fallback to detection if connector doesn't match
741
+ return detectBtcWallet();
742
+ }
743
+
744
+ const createPsbtOptions = (_: any, request: any) => {
745
+ var _a;
746
+ const psbtSignOptions: any = {
747
+ autoFinalized: false,
748
+ };
749
+ if (request.signature) {
750
+ // validatePsbt(psbt, request.allowedSighash, request.signature);
751
+
752
+ const toSignInputs = [];
753
+ for (const signature of request.signature) {
754
+ if ((_a = signature.signingIndexes) === null || _a === void 0 ? void 0 : _a.length) {
755
+ for (const index of signature.signingIndexes) {
756
+ toSignInputs.push({
757
+ address: signature.address,
758
+ disableAddressValidation: signature.disableAddressValidation,
759
+ index,
760
+ sighashTypes: request.allowedSighash,
761
+ });
762
+ }
763
+ }
764
+ }
765
+ psbtSignOptions.toSignInputs = toSignInputs;
766
+ }
767
+ return psbtSignOptions;
768
+ };
769
+
770
+ // Helper function to convert hex response to base64
771
+ function convertHexToBase64(hexString: string): string {
772
+ try {
773
+ const buffer = Buffer.from(hexString, 'hex');
774
+ return buffer.toString('base64');
775
+ } catch (error) {
776
+ // If conversion fails, it might already be base64
777
+ return hexString;
778
+ }
779
+ }
689
780
 
781
+ // Helper function to extract signed PSBT from wallet response
782
+ function extractSignedPsbt(response: any): string | null {
783
+ if (!response) return null;
784
+ if (typeof response === 'string') return response;
785
+ return response.signedPsbtHex || response.signedPsbtBase64 || response.signedPsbt || null;
786
+ }
787
+
788
+ async function executeBitcoinSwap(
789
+ step: ExtendedOpenOceanStep,
790
+ options: ExecuteRouteOptions,
791
+ process: Process,
792
+ route: ExtendedRoute
793
+ ): Promise<void> {
794
+ try {
795
+
796
+ const { quoteData } = step || {}
797
+
798
+ const adaptedWallet: any = adaptBitcoinWallet(
799
+ options.account.address?.toString() || '',
800
+ async (_: any, __: any, dynamicParams: DynamicSignPsbtParams) => {
801
+ const psbtFromBase64 = bitcoin.Psbt.fromBase64(dynamicParams.unsignedPsbtBase64);
802
+ const psbtHex = psbtFromBase64.toHex();
803
+ const walletType = detectBtcWallet();
804
+ const anyWindow = typeof window !== 'undefined' ? (window as any) : undefined
805
+ switch (walletType) {
806
+ case 'okx': {
807
+ const response = await anyWindow.okxwallet?.bitcoin?.signPsbt(
808
+ psbtHex,
809
+ createPsbtOptions(psbtFromBase64, dynamicParams)
810
+ );
811
+ const signedPsbt = extractSignedPsbt(response);
812
+ if (!signedPsbt) {
813
+ throw new Error('Missing psbt response from OKX wallet');
814
+ }
815
+ return convertHexToBase64(signedPsbt);
816
+ }
817
+
818
+ case 'unisat': {
819
+ const response = await anyWindow.unisat?.signPsbt(
820
+ psbtHex,
821
+ createPsbtOptions(psbtFromBase64, dynamicParams)
822
+ );
823
+ const signedPsbt = extractSignedPsbt(response);
824
+ if (!signedPsbt) {
825
+ throw new Error('Missing psbt response from Unisat wallet');
826
+ }
827
+ return convertHexToBase64(signedPsbt);
828
+ }
829
+
830
+ case 'xverse': {
831
+ const response = await anyWindow.BitcoinProvider?.request('signPsbt', {
832
+ psbt: psbtHex,
833
+ finalize: true,
834
+ });
835
+ const signedPsbt = extractSignedPsbt(response);
836
+ if (!signedPsbt) {
837
+ throw new Error('Missing psbt response from Xverse wallet');
838
+ }
839
+ return convertHexToBase64(signedPsbt);
840
+ }
841
+
842
+ default:
843
+ throw new Error(`Unsupported wallet: ${walletType}`);
844
+ }
845
+ }
846
+ );
847
+ adaptedWallet.sendTransaction = async (params: { recipient: string; amount: string | number }) => {
848
+ const walletType = detectBtcWallet();
849
+ const anyWindow = typeof window !== 'undefined' ? (window as any) : undefined
850
+
851
+ // Convert amount to satoshis (BTC amount * 100000000)
852
+ const amountInSatoshis = Number(params.amount)
853
+ switch (walletType) {
854
+ case 'okx': {
855
+ // OKX wallet sendBitcoin method
856
+ if (anyWindow.okxwallet?.bitcoin?.sendBitcoin) {
857
+ const txid = await anyWindow.okxwallet.bitcoin.sendBitcoin(
858
+ params.recipient,
859
+ amountInSatoshis
860
+ );
861
+ return txid;
862
+ }
863
+ throw new Error('OKX wallet does not support sendBitcoin');
864
+ }
865
+
866
+ case 'unisat': {
867
+ // Unisat wallet sendBitcoin method
868
+ if (anyWindow.unisat?.sendBitcoin) {
869
+ const txid = await anyWindow.unisat.sendBitcoin(
870
+ params.recipient,
871
+ amountInSatoshis
872
+ );
873
+ return txid;
874
+ }
875
+ throw new Error('Unisat wallet does not support sendBitcoin');
876
+ }
877
+
878
+ case 'xverse': {
879
+ // Xverse wallet sendBitcoin method
880
+ if (anyWindow.BitcoinProvider?.request) {
881
+ const response = await anyWindow.BitcoinProvider.request('sendBitcoin', {
882
+ address: params.recipient,
883
+ amount: amountInSatoshis,
884
+ });
885
+ return response.txid || response;
886
+ }
887
+ throw new Error('Xverse wallet does not support sendBitcoin');
888
+ }
889
+
890
+ default:
891
+ throw new Error(`Unsupported wallet: ${walletType}`);
892
+ }
893
+ }
894
+
895
+ const signedTx = await bridgeExecuteSwap({
896
+ quoteData: quoteData,
897
+ walletClient: adaptedWallet,
898
+ });
899
+
900
+ if (!signedTx) {
901
+ throw new Error('Failed to sign transaction')
902
+ }
903
+ const hash = signedTx.sourceTxHash
904
+
905
+ process.status = 'DONE'
906
+ process.doneAt = Date.now()
907
+ process.txHash = hash
908
+ process.message = 'Transaction confirmed'
909
+ options.updateRouteHook?.(route)
910
+
911
+ // process.status = 'PENDING'
912
+ // process.txHash = hash
913
+ // process.message = 'Transaction pending'
914
+ // options.updateRouteHook?.(route)
915
+
916
+
917
+ } catch (error) {
918
+ console.error('Bitcoin swap execution failed:', error)
919
+ process.status = 'FAILED'
920
+ process.error =
921
+ error instanceof Error || (error && (error as any)?.message)
922
+ ? {
923
+ code: 'EXECUTION_ERROR',
924
+ message: error instanceof Error ? error.message : 'Unknown error occurred',
925
+ htmlMessage: error instanceof Error ? error.message : 'Unknown error occurred',
926
+ }
927
+ : {
928
+ code: 'UNKNOWN_ERROR',
929
+ message: 'Unknown error occurred',
930
+ htmlMessage: 'Unknown error occurred',
931
+ }
932
+ step.execution!.status = 'FAILED'
933
+ options.updateRouteHook?.(route)
934
+ throw error
935
+ }
936
+ }
690
937
  // Execute transaction
691
938
  async function executeSwap(
692
939
  route: ExtendedRoute,
@@ -730,6 +977,10 @@ async function executeSwap(
730
977
  const currentStep = route.steps[0]
731
978
  if (currentStep.action?.fromChainId === 1151111081099710) {
732
979
  await executeSolanaSwap(currentStep, options, process, updatedRoute)
980
+ } else if (currentStep.action?.fromChainId === 20000000000001) {
981
+ await executeBitcoinSwap(currentStep, options, process, updatedRoute)
982
+ } else if (currentStep.action?.fromChainId === 20000000000001) {
983
+ await executeBitcoinSwap(currentStep, options, process, updatedRoute)
733
984
  } else {
734
985
  await executeEvmSwap(currentStep, options, process, updatedRoute)
735
986
  }
@@ -17,6 +17,8 @@ export class OpenOceanService {
17
17
  // Chain ID to OpenOcean chain name mapping
18
18
  private static readonly CHAIN_ID_MAP: Record<string | number, string> = {
19
19
  1151111081099710: 'solana', // Solana mainnet
20
+ 20000000000001: 'bitcoin', // Bitcoin mainnet
21
+ 20000000000006: 'near', // Near mainnet
20
22
  }
21
23
 
22
24
  // Get OpenOcean supported chain name
@@ -125,7 +127,21 @@ export class OpenOceanService {
125
127
  const response = await fetch(`${this.API_V4_URL}/${chainName}/tokenList`)
126
128
  const data = await response.json()
127
129
  if (data.code !== 200) {
128
- throw new Error('Failed to fetch token list')
130
+ if (chain === '20000000000001') {
131
+ data.data = [
132
+ {
133
+ address: 'bitcoin',
134
+ symbol: 'BTC',
135
+ decimals: 8,
136
+ isNative: true,
137
+ name: 'Bitcoin',
138
+ icon: 'https://assets.coingecko.com/coins/images/1/standard/bitcoin.png',
139
+ usd: '109559',
140
+ }
141
+ ]
142
+ } else {
143
+ throw new Error('Failed to fetch token list')
144
+ }
129
145
  }
130
146
  return data.data.map((token: OpenOceanToken) => {
131
147
  let address = token.address
@@ -159,7 +175,7 @@ export class OpenOceanService {
159
175
  }
160
176
 
161
177
  static async getGasPrice(chain: string) {
162
- if (!chain || chain === '1151111081099710') {
178
+ if (!chain || chain === '1151111081099710' || chain === '20000000000001') {
163
179
  return {
164
180
  data: {
165
181
  gasPrice: '1000000000000000000',
@@ -177,7 +193,7 @@ export class OpenOceanService {
177
193
  // Check if the address is valid
178
194
  if (!tokenAddress || (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress) && !/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(tokenAddress))) {
179
195
  throw new Error('Invalid token address')
180
- }
196
+ }
181
197
  const response = await fetch(`${this.API_V4_URL}/${chainName}/getTokenInfo?tokenAddress=${tokenAddress}`)
182
198
  const data = await response.json()
183
199
  if (!data || !data.address || !data.symbol || !data.decimals) {
@@ -204,22 +220,30 @@ export class OpenOceanService {
204
220
  * @returns Promise<Record<string, string>> returns an object where key is token address and value is price
205
221
  */
206
222
  static async getTokensPrice(chain: string, tokenAddresses: string[]): Promise<Record<string, string>> {
207
- const chainName = this.getChainName(chain)
223
+ let chainName = this.getChainName(chain)
224
+ if (chain === '20000000000001') {
225
+ tokenAddresses = ['0x2260fac5e5542a773aa44fbcfedf7c193bc2c599']
226
+ chainName = '1'
227
+ }
228
+
208
229
  const tokenAddressesStr = tokenAddresses.join(',')
209
230
  const response = await fetch(`${this.API_V3_URL}/${chainName}/designated_tokenList?tokens=${tokenAddressesStr}`)
210
231
  const data = await response.json()
211
-
232
+
212
233
  if (data.code !== 200) {
213
234
  throw new Error('Failed to fetch token prices')
214
235
  }
215
236
 
216
237
  const prices = data.data.reduce((acc: Record<string, string>, token: OpenOceanToken) => {
217
238
  if (token.address && token.usd) {
218
- acc[token.address.toLowerCase()] = token.usd
239
+ if (chain === '20000000000001') {
240
+ acc.bitcoin = token.usd
241
+ } else {
242
+ acc[token.address.toLowerCase()] = token.usd
243
+ }
219
244
  }
220
245
  return acc
221
246
  }, {})
222
-
223
247
  return prices
224
248
  }
225
249
  }
@@ -20,40 +20,40 @@ export const createBookmarksStore = ({
20
20
  selectedBookmark: toAddress,
21
21
  bookmarks: [],
22
22
  recentWallets: [],
23
- getBookmark: (address) =>
24
- (get() as any).bookmarks.find((bookmark) => bookmark.address === address),
25
- addBookmark: (bookmark) => {
26
- set((state) => ({
23
+ getBookmark: (address: string) =>
24
+ (get() as any).bookmarks.find((bookmark: any) => bookmark.address === address),
25
+ addBookmark: (bookmark: any) => {
26
+ set((state: any) => ({
27
27
  bookmarks: [bookmark, ...state.bookmarks],
28
28
  }))
29
29
  },
30
- removeBookmark: (address) => {
31
- set((state) => ({
30
+ removeBookmark: (address: string) => {
31
+ set((state: any) => ({
32
32
  bookmarks: state.bookmarks.filter(
33
- (storedBookmark) => storedBookmark.address !== address
33
+ (storedBookmark: any) => storedBookmark.address !== address
34
34
  ),
35
35
  }))
36
36
  },
37
37
  getSelectedBookmark: () => (get() as any).selectedBookmark,
38
- setSelectedBookmark: (bookmark) => {
39
- set((_state) => ({
38
+ setSelectedBookmark: (bookmark: any) => {
39
+ set((_state: any) => ({
40
40
  selectedBookmark: bookmark,
41
41
  }))
42
42
  },
43
- addRecentWallet: (bookmark) => {
44
- set((state) => ({
43
+ addRecentWallet: (bookmark: any) => {
44
+ set((state: any) => ({
45
45
  recentWallets: [
46
46
  bookmark,
47
47
  ...state.recentWallets.filter(
48
- (recentWallet) => recentWallet.address !== bookmark.address
48
+ (recentWallet: any) => recentWallet.address !== bookmark.address
49
49
  ),
50
50
  ].slice(0, recentWalletsLimit),
51
51
  }))
52
52
  },
53
- removeRecentWallet: (address) => {
54
- set((state) => ({
53
+ removeRecentWallet: (address: string) => {
54
+ set((state: any) => ({
55
55
  recentWallets: state.recentWallets.filter(
56
- (storedRecent) => storedRecent.address !== address
56
+ (storedRecent: any) => storedRecent.address !== address
57
57
  ),
58
58
  }))
59
59
  },
@@ -2,11 +2,34 @@ import { isUTXOAddress } from '@bigmi/core'
2
2
  import { ChainId, ChainType, isSVMAddress } from '@openocean.finance/widget-sdk'
3
3
  import { isAddress as isEVMAddress } from 'viem'
4
4
 
5
- const chainTypeAddressValidation = {
5
+ const isNearAddress = (address: string) => {
6
+ if (!address) {
7
+ return false
8
+ }
9
+
10
+ // Implicit accounts are 64-char hex strings
11
+ const isImplicitAccount = /^[0-9a-f]{64}$/i.test(address)
12
+ if (isImplicitAccount) {
13
+ return true
14
+ }
15
+
16
+ // Named accounts must be lowercase and end with a TLD (e.g. .near, .testnet, custom subdomain)
17
+ const isNamedAccount =
18
+ /^[a-z0-9_-]+(\.[a-z0-9_-]+)*(\.near|\.testnet|\.chain|\.btc)?$/.test(
19
+ address
20
+ )
21
+ return isNamedAccount
22
+ }
23
+
24
+ const chainTypeAddressValidation: Record<
25
+ ChainType,
26
+ (address: string) => boolean
27
+ > = {
6
28
  [ChainType.EVM]: isEVMAddress,
7
29
  [ChainType.SVM]: isSVMAddress,
8
30
  [ChainType.UTXO]: isUTXOAddress,
9
31
  [ChainType.MVM]: () => false,
32
+ [ChainType.NVM]: isNearAddress,
10
33
  }
11
34
 
12
35
  export const getChainTypeFromAddress = (
@@ -26,4 +49,5 @@ export const defaultChainIdsByType = {
26
49
  [ChainType.SVM]: ChainId.SOL,
27
50
  [ChainType.UTXO]: ChainId.BTC,
28
51
  [ChainType.MVM]: ChainId.SUI,
52
+ [ChainType.NVM]: ChainId.NEAR,
29
53
  }
@@ -28,9 +28,9 @@ export const getPriceImpact = ({
28
28
  if (!fromTokenPrice || !toTokenPrice) {
29
29
  return 0
30
30
  }
31
- console.log('fromTokenPrice', fromTokenPrice)
32
- console.log('toTokenPrice', toTokenPrice)
33
- console.log('fromTokenPrice / toTokenPrice', fromTokenPrice / toTokenPrice)
31
+ // console.log('fromTokenPrice', fromTokenPrice)
32
+ // console.log('toTokenPrice', toTokenPrice)
33
+ // console.log('fromTokenPrice / toTokenPrice', fromTokenPrice / toTokenPrice)
34
34
  const priceImpact = toTokenPrice / fromTokenPrice - 1
35
35
 
36
36
  return priceImpact