@openocean.finance/widget 1.0.41 → 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.
- package/dist/esm/App.js.map +1 -1
- package/dist/esm/AppDrawer.style.d.ts +1 -1
- package/dist/esm/components/ActiveTransactions/ActiveTransactions.style.d.ts +2 -2
- package/dist/esm/components/AmountInput/AmountInput.style.d.ts +1 -1
- package/dist/esm/components/AmountInput/AmountInputAdornment.style.d.ts +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +37 -33
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/Avatar/Avatar.style.d.ts +1 -1
- package/dist/esm/components/Avatar/SmallAvatar.d.ts +1 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +1 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
- package/dist/esm/components/ButtonTertiary.d.ts +1 -1
- package/dist/esm/components/Card/CardHeader.d.ts +1 -1
- package/dist/esm/components/Card/CardIconButton.d.ts +1 -1
- package/dist/esm/components/ContractComponent/NFT/NFT.style.d.ts +1 -1
- package/dist/esm/components/Header/Header.style.d.ts +2 -2
- package/dist/esm/components/Header/SettingsButton.style.d.ts +2 -2
- package/dist/esm/components/ListItem/ListItem.d.ts +1 -1
- package/dist/esm/components/Messages/WarningMessages.js +1 -0
- package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
- package/dist/esm/components/Messages/useMessageQueue.js +1 -1
- package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
- package/dist/esm/components/Search/SearchInput.style.d.ts +1 -1
- package/dist/esm/components/SelectTokenButton/SelectTokenButton.style.d.ts +1 -1
- package/dist/esm/components/SendToWallet/SendToWallet.style.d.ts +1 -1
- package/dist/esm/components/Skeleton/WidgetSkeleton.style.d.ts +2 -2
- package/dist/esm/components/StepActions/StepActions.style.d.ts +1 -1
- package/dist/esm/components/Tabs/Tabs.style.d.ts +2 -2
- package/dist/esm/components/TokenList/TokenList.style.d.ts +2 -2
- package/dist/esm/components/TransactionDetails.js +1 -1
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/defaultChainIds.js +3 -0
- package/dist/esm/config/defaultChainIds.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/AcrossAdapter.js +21 -22
- package/dist/esm/cross/adapters/AcrossAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/BaseSwapAdapter.d.ts +17 -9
- package/dist/esm/cross/adapters/BaseSwapAdapter.js +15 -4
- package/dist/esm/cross/adapters/BaseSwapAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/DebridgeAdapter.js +8 -7
- package/dist/esm/cross/adapters/DebridgeAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/LifiAdapter.js +7 -6
- package/dist/esm/cross/adapters/LifiAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/MayanAdapter.js +1 -2
- package/dist/esm/cross/adapters/MayanAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/NearIntentsAdapter.d.ts +13 -6
- package/dist/esm/cross/adapters/NearIntentsAdapter.js +252 -75
- package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/OptimexAdapter.js +2 -1
- package/dist/esm/cross/adapters/OptimexAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/OrbiterAdapter.js +9 -8
- package/dist/esm/cross/adapters/OrbiterAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/RelayAdapter.js +46 -15
- package/dist/esm/cross/adapters/RelayAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/SymbiosisAdapter.js +13 -14
- package/dist/esm/cross/adapters/SymbiosisAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/XYFinanceAdapter.js +13 -17
- package/dist/esm/cross/adapters/XYFinanceAdapter.js.map +1 -1
- package/dist/esm/cross/adapters/index.d.ts +2 -0
- package/dist/esm/cross/adapters/index.js +2 -1
- package/dist/esm/cross/adapters/index.js.map +1 -1
- package/dist/esm/cross/constants/index.d.ts +3 -1
- package/dist/esm/cross/constants/index.js +2 -0
- package/dist/esm/cross/constants/index.js.map +1 -1
- package/dist/esm/cross/crossChainQuote.d.ts +1 -1
- package/dist/esm/cross/crossChainQuote.js +22 -12
- package/dist/esm/cross/crossChainQuote.js.map +1 -1
- package/dist/esm/cross/factory.d.ts +3 -1
- package/dist/esm/cross/factory.js +10 -12
- package/dist/esm/cross/factory.js.map +1 -1
- package/dist/esm/cross/registry.d.ts +1 -1
- package/dist/esm/hooks/useAvailableChains.js +47 -40
- package/dist/esm/hooks/useAvailableChains.js.map +1 -1
- package/dist/esm/hooks/useChains.js.map +1 -1
- package/dist/esm/hooks/useRoutes.js +11 -3
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useTokenAddressBalance.js.map +1 -1
- package/dist/esm/hooks/useTokens.d.ts +1 -0
- package/dist/esm/hooks/useTokens.js +14 -0
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pages/MainPage/MainPage.js +1 -2
- package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
- package/dist/esm/pages/MainPage/MainWarningMessages.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +2 -2
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +2 -2
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToWalletPage.style.d.ts +4 -4
- package/dist/esm/pages/SettingsPage/SettingsCard/SettingCard.style.d.ts +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/services/ExecuteRoute.js +218 -9
- package/dist/esm/services/ExecuteRoute.js.map +1 -1
- package/dist/esm/services/OpenOceanService.js +30 -4
- package/dist/esm/services/OpenOceanService.js.map +1 -1
- package/dist/esm/stores/bookmarks/createBookmarkStore.js.map +1 -1
- package/dist/esm/utils/chainType.d.ts +1 -0
- package/dist/esm/utils/chainType.js +15 -0
- package/dist/esm/utils/chainType.js.map +1 -1
- package/dist/esm/utils/getPriceImpact.js +3 -3
- package/dist/esm/utils/getPriceImpact.js.map +1 -1
- package/package.json +18 -6
- package/src/App.tsx +0 -1
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +39 -34
- package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +3 -2
- package/src/components/Messages/WarningMessages.tsx +1 -0
- package/src/components/Messages/useMessageQueue.ts +1 -1
- package/src/components/TransactionDetails.tsx +4 -4
- package/src/config/defaultChainIds.ts +3 -0
- package/src/config/version.ts +1 -1
- package/src/cross/adapters/AcrossAdapter.ts +21 -22
- package/src/cross/adapters/BaseSwapAdapter.ts +24 -8
- package/src/cross/adapters/DebridgeAdapter.ts +11 -11
- package/src/cross/adapters/LifiAdapter.ts +11 -10
- package/src/cross/adapters/MayanAdapter.ts +1 -2
- package/src/cross/adapters/NearIntentsAdapter.ts +303 -129
- package/src/cross/adapters/OptimexAdapter.ts +12 -11
- package/src/cross/adapters/OrbiterAdapter.ts +17 -16
- package/src/cross/adapters/RelayAdapter.ts +50 -17
- package/src/cross/adapters/SymbiosisAdapter.ts +13 -14
- package/src/cross/adapters/XYFinanceAdapter.ts +15 -19
- package/src/cross/adapters/index.ts +2 -1
- package/src/cross/constants/index.ts +4 -0
- package/src/cross/crossChainQuote.ts +27 -21
- package/src/cross/factory.ts +12 -12
- package/src/cross/registry.ts +1 -1
- package/src/hooks/useAvailableChains.ts +50 -43
- package/src/hooks/useChains.ts +0 -1
- package/src/hooks/useExplorer.ts +6 -6
- package/src/hooks/useRoutes.ts +12 -4
- package/src/hooks/useTokenAddressBalance.ts +1 -1
- package/src/hooks/useTokens.ts +20 -5
- package/src/index.ts +0 -1
- package/src/pages/MainPage/MainPage.tsx +1 -1
- package/src/pages/MainPage/MainWarningMessages.tsx +0 -1
- package/src/pages/SendToWallet/BookmarksPage.tsx +1 -1
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +2 -2
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +2 -2
- package/src/providers/WidgetProvider/WidgetProvider.tsx +0 -1
- package/src/services/ExecuteRoute.ts +315 -64
- package/src/services/OpenOceanService.ts +31 -7
- package/src/stores/bookmarks/createBookmarkStore.ts +15 -15
- package/src/utils/chainType.ts +25 -1
- package/src/utils/getPriceImpact.ts +3 -3
- 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
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
436
|
+
code: 'EXECUTION_ERROR',
|
|
437
|
+
message: error.message,
|
|
438
|
+
htmlMessage: error.message,
|
|
439
|
+
}
|
|
422
440
|
: {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
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
|
-
|
|
550
|
-
|
|
565
|
+
route?.data?.prependedOperatingExpenseCost ||
|
|
566
|
+
'0'
|
|
551
567
|
)
|
|
552
568
|
if (allowance < BigInt(amount)) {
|
|
553
569
|
const approvalAmount = options.infiniteApproval
|
|
554
570
|
? BigInt(
|
|
555
|
-
|
|
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
|
|
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
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
691
|
+
code: 'EXECUTION_ERROR',
|
|
692
|
+
message: error.message,
|
|
693
|
+
htmlMessage: error.message,
|
|
694
|
+
}
|
|
679
695
|
: {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|
package/src/utils/chainType.ts
CHANGED
|
@@ -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
|
|
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
|