@shapeshiftoss/swap-widget 0.0.2 → 0.2.0

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 (158) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +18 -11
  3. package/package.json +77 -28
  4. package/Dockerfile +0 -87
  5. package/dist/assets/PhArrowCircleDown-B305t-Sh.js +0 -14
  6. package/dist/assets/PhArrowClockwise-CJPxQhr_.js +0 -14
  7. package/dist/assets/PhArrowDown-C4hT9BVu.js +0 -14
  8. package/dist/assets/PhArrowLeft-Way4Cu-r.js +0 -14
  9. package/dist/assets/PhArrowRight-Cp5ZME0F.js +0 -14
  10. package/dist/assets/PhArrowSquareOut-NnrK69c5.js +0 -14
  11. package/dist/assets/PhArrowUp-Dph6j8rm.js +0 -14
  12. package/dist/assets/PhArrowUpRight-BpJjRRRh.js +0 -14
  13. package/dist/assets/PhArrowsClockwise-CFUT8X7R.js +0 -14
  14. package/dist/assets/PhArrowsDownUp-BjBI4ckC.js +0 -14
  15. package/dist/assets/PhArrowsLeftRight-CuLpbKRk.js +0 -14
  16. package/dist/assets/PhBank-Dchr4XQJ.js +0 -14
  17. package/dist/assets/PhBrowser-dUu2TrC1.js +0 -14
  18. package/dist/assets/PhCaretDown-78IuKU3V.js +0 -14
  19. package/dist/assets/PhCaretLeft-_aiyH7n1.js +0 -14
  20. package/dist/assets/PhCaretRight-8zJQEuDX.js +0 -14
  21. package/dist/assets/PhCaretUp-CiCxKWwN.js +0 -14
  22. package/dist/assets/PhCheck-CcADx8br.js +0 -14
  23. package/dist/assets/PhCircleHalf-BYzSzd2h.js +0 -14
  24. package/dist/assets/PhClock-UItiXeJq.js +0 -14
  25. package/dist/assets/PhCompass-CUy3uA7t.js +0 -14
  26. package/dist/assets/PhCopy-C-PDgSlZ.js +0 -14
  27. package/dist/assets/PhCreditCard-CWRaUT6F.js +0 -14
  28. package/dist/assets/PhCurrencyDollar-Cqla3AXT.js +0 -14
  29. package/dist/assets/PhDesktop-BBIPrYLl.js +0 -14
  30. package/dist/assets/PhDeviceMobile-BdsPaATg.js +0 -14
  31. package/dist/assets/PhDotsThree-BMXCQl-0.js +0 -14
  32. package/dist/assets/PhEnvelope-yUsis6yU.js +0 -14
  33. package/dist/assets/PhFunnelSimple-Ba7BbWTp.js +0 -14
  34. package/dist/assets/PhGlobe-DgLxBk5U.js +0 -14
  35. package/dist/assets/PhIdentificationCard-Czjmq9Z3.js +0 -14
  36. package/dist/assets/PhImage-BAJ_erz6.js +0 -14
  37. package/dist/assets/PhInfo-Cr4DhVUE.js +0 -14
  38. package/dist/assets/PhLightbulb-BX_7duTr.js +0 -14
  39. package/dist/assets/PhMagnifyingGlass-Mygfryvz.js +0 -14
  40. package/dist/assets/PhPaperPlaneRight-BeuCA660.js +0 -14
  41. package/dist/assets/PhPlus-BMjcHjsT.js +0 -14
  42. package/dist/assets/PhPower-CGffGmdb.js +0 -14
  43. package/dist/assets/PhPuzzlePiece-wm_GpVSi.js +0 -14
  44. package/dist/assets/PhQrCode-DRwRzXCH.js +0 -14
  45. package/dist/assets/PhQuestion-M09j2U0T.js +0 -14
  46. package/dist/assets/PhQuestionMark-BP9EMB6h.js +0 -14
  47. package/dist/assets/PhSealCheck-BfG80yKn.js +0 -14
  48. package/dist/assets/PhSignOut-C5YOWlPT.js +0 -14
  49. package/dist/assets/PhSpinner-BXooDYzU.js +0 -14
  50. package/dist/assets/PhTrash-Biq6rmmL.js +0 -14
  51. package/dist/assets/PhUser-CAgZSpGe.js +0 -14
  52. package/dist/assets/PhVault-l2PHF38a.js +0 -14
  53. package/dist/assets/PhWallet-w25D16It.js +0 -14
  54. package/dist/assets/PhWarning-DPEydIPw.js +0 -14
  55. package/dist/assets/PhWarningCircle-BqjT745H.js +0 -14
  56. package/dist/assets/PhX-s-SmVExa.js +0 -14
  57. package/dist/assets/SwapController-If0bFrNc.js +0 -1
  58. package/dist/assets/ccip-BBB7uCdL.js +0 -1
  59. package/dist/assets/ccip-CkkdfKKI.js +0 -1
  60. package/dist/assets/data-capture-D4gAy-Yc.js +0 -242
  61. package/dist/assets/email-B1AJehis.js +0 -90
  62. package/dist/assets/embedded-wallet-Q6DQopDk.js +0 -410
  63. package/dist/assets/features-B6JAPAFb.js +0 -5
  64. package/dist/assets/index-5jd8RPN9.js +0 -7428
  65. package/dist/assets/index-B4kNtSTk.js +0 -1080
  66. package/dist/assets/index-B753eAob.js +0 -1
  67. package/dist/assets/index-BZoLTv77.js +0 -3
  68. package/dist/assets/index-Bcw9UURX.js +0 -3
  69. package/dist/assets/index-BkrV-ahK.js +0 -2
  70. package/dist/assets/index-CRQzQ_up.js +0 -9
  71. package/dist/assets/index-CSW0Gt5y.js +0 -132
  72. package/dist/assets/index-CZ4jTIsk.js +0 -138
  73. package/dist/assets/index-Cwa8SB3h.js +0 -61
  74. package/dist/assets/index-DEwQgT4r.js +0 -78
  75. package/dist/assets/index-DVfQiQ1d.js +0 -1
  76. package/dist/assets/index-cV3UdaMs.css +0 -1
  77. package/dist/assets/index-hAmu622O.js +0 -43
  78. package/dist/assets/onramp-XcPNCLRU.js +0 -422
  79. package/dist/assets/pay-with-exchange-BeHt4OF4.js +0 -280
  80. package/dist/assets/property-C9JG5tag.js +0 -27
  81. package/dist/assets/receive-7ff4he2S.js +0 -131
  82. package/dist/assets/send-CkMBIRXT.js +0 -554
  83. package/dist/assets/socials-DmmxuLrP.js +0 -250
  84. package/dist/assets/swaps-IHAYRQ0x.js +0 -1050
  85. package/dist/assets/transactions-9Cxdk7o9.js +0 -16
  86. package/dist/assets/v4-BOvFkHkt.js +0 -1
  87. package/dist/assets/w3m-modal-kJzFnBfB.js +0 -891
  88. package/dist/assets/wallet-standard-Dv8LMoUB.js +0 -1
  89. package/dist/favicon.png +0 -0
  90. package/dist/favicon.svg +0 -11
  91. package/dist/icon-192x192.png +0 -0
  92. package/dist/icon-512x512.png +0 -0
  93. package/dist/index.html +0 -33
  94. package/index.html +0 -32
  95. package/public/favicon.png +0 -0
  96. package/public/favicon.svg +0 -11
  97. package/public/icon-192x192.png +0 -0
  98. package/public/icon-512x512.png +0 -0
  99. package/railway.toml +0 -10
  100. package/src/api/client.ts +0 -95
  101. package/src/components/AddressInputModal.css +0 -173
  102. package/src/components/AddressInputModal.tsx +0 -250
  103. package/src/components/ApprovalStep.tsx +0 -92
  104. package/src/components/ExecutionStep.tsx +0 -27
  105. package/src/components/InputStep.tsx +0 -431
  106. package/src/components/QuoteSelector.css +0 -146
  107. package/src/components/QuoteSelector.tsx +0 -137
  108. package/src/components/QuotesModal.css +0 -250
  109. package/src/components/QuotesModal.tsx +0 -182
  110. package/src/components/SettingsModal.css +0 -145
  111. package/src/components/SettingsModal.tsx +0 -180
  112. package/src/components/StatusStep.tsx +0 -139
  113. package/src/components/SwapWidget.css +0 -713
  114. package/src/components/SwapWidget.tsx +0 -607
  115. package/src/components/TokenSelectModal.css +0 -438
  116. package/src/components/TokenSelectModal.tsx +0 -487
  117. package/src/components/WalletProvider.tsx +0 -92
  118. package/src/config/appkit.ts +0 -104
  119. package/src/config/wagmi.ts +0 -27
  120. package/src/constants/chains.ts +0 -72
  121. package/src/constants/defaults.ts +0 -28
  122. package/src/constants/swappers.ts +0 -48
  123. package/src/constants/viemChains.ts +0 -69
  124. package/src/contexts/SwapWalletContext.tsx +0 -25
  125. package/src/demo/App.css +0 -458
  126. package/src/demo/App.tsx +0 -481
  127. package/src/demo/main.tsx +0 -25
  128. package/src/hooks/useAssets.ts +0 -180
  129. package/src/hooks/useBalances.ts +0 -822
  130. package/src/hooks/useBitcoinSigning.ts +0 -276
  131. package/src/hooks/useMarketData.ts +0 -169
  132. package/src/hooks/useSolanaSigning.ts +0 -275
  133. package/src/hooks/useStatusPolling.ts +0 -137
  134. package/src/hooks/useSwapApproval.ts +0 -106
  135. package/src/hooks/useSwapDisplayValues.ts +0 -157
  136. package/src/hooks/useSwapExecution.ts +0 -199
  137. package/src/hooks/useSwapHandlers.ts +0 -119
  138. package/src/hooks/useSwapQuote.ts +0 -71
  139. package/src/hooks/useSwapQuoting.ts +0 -102
  140. package/src/hooks/useSwapRates.ts +0 -61
  141. package/src/index.ts +0 -43
  142. package/src/machines/SwapMachineContext.ts +0 -5
  143. package/src/machines/__tests__/guards.test.ts +0 -228
  144. package/src/machines/__tests__/setup.test.ts +0 -7
  145. package/src/machines/__tests__/swapMachine.test.ts +0 -544
  146. package/src/machines/__tests__/types.test.ts +0 -124
  147. package/src/machines/guards.ts +0 -33
  148. package/src/machines/swapMachine.ts +0 -288
  149. package/src/machines/types.ts +0 -54
  150. package/src/services/transactionStatus.ts +0 -322
  151. package/src/types/index.ts +0 -361
  152. package/src/utils/addressValidation.ts +0 -201
  153. package/src/utils/redirect.ts +0 -45
  154. package/src/vite-env.d.ts +0 -10
  155. package/tsconfig.json +0 -23
  156. package/tsconfig.node.json +0 -10
  157. package/vite.config.ts +0 -101
  158. package/vitest.config.ts +0 -18
@@ -1,487 +0,0 @@
1
- import './TokenSelectModal.css'
2
-
3
- import { bnOrZero } from '@shapeshiftoss/utils'
4
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
- import { Virtuoso } from 'react-virtuoso'
6
-
7
- import type { ChainInfo } from '../hooks/useAssets'
8
- import { useAssets, useChains } from '../hooks/useAssets'
9
- import { useMultiChainBalances } from '../hooks/useBalances'
10
- import { useBitcoinSigning } from '../hooks/useBitcoinSigning'
11
- import { useAllMarketData } from '../hooks/useMarketData'
12
- import { useSolanaSigning } from '../hooks/useSolanaSigning'
13
- import type { Asset, AssetId, ChainId } from '../types'
14
-
15
- const VISIBLE_BUFFER = 10
16
-
17
- const useLockBodyScroll = (isLocked: boolean) => {
18
- useEffect(() => {
19
- if (!isLocked) return
20
- const originalOverflow = document.body.style.overflow
21
- document.body.style.overflow = 'hidden'
22
- return () => {
23
- document.body.style.overflow = originalOverflow
24
- }
25
- }, [isLocked])
26
- }
27
-
28
- type TokenSelectModalProps = {
29
- isOpen: boolean
30
- onClose: () => void
31
- onSelect: (asset: Asset) => void
32
- disabledAssetIds?: string[]
33
- disabledChainIds?: ChainId[]
34
- allowedChainIds?: ChainId[]
35
- allowedAssetIds?: AssetId[]
36
- walletAddress?: string
37
- currentAssetIds?: AssetId[]
38
- }
39
-
40
- const isNativeAsset = (assetId: string): boolean => {
41
- return assetId.includes('/slip44:') || assetId.endsWith('/native')
42
- }
43
-
44
- const scoreAsset = (asset: Asset, query: string): number => {
45
- const lowerQuery = query.toLowerCase()
46
- const symbolLower = asset.symbol.toLowerCase()
47
- const nameLower = asset.name.toLowerCase()
48
-
49
- let score = 0
50
-
51
- if (symbolLower === lowerQuery) score += 1000
52
- else if (symbolLower.startsWith(lowerQuery)) score += 500
53
- else if (symbolLower.includes(lowerQuery)) score += 100
54
-
55
- if (nameLower === lowerQuery) score += 800
56
- else if (nameLower.startsWith(lowerQuery)) score += 300
57
- else if (nameLower.includes(lowerQuery)) score += 50
58
-
59
- if (isNativeAsset(asset.assetId)) score += 200
60
-
61
- return score
62
- }
63
-
64
- export const TokenSelectModal = ({
65
- isOpen,
66
- onClose,
67
- onSelect,
68
- disabledAssetIds = [],
69
- disabledChainIds = [],
70
- allowedChainIds,
71
- allowedAssetIds,
72
- walletAddress,
73
- currentAssetIds = [],
74
- }: TokenSelectModalProps) => {
75
- useLockBodyScroll(isOpen)
76
- const [searchQuery, setSearchQuery] = useState('')
77
- const [chainSearchQuery, setChainSearchQuery] = useState('')
78
- const [selectedChainId, setSelectedChainId] = useState<ChainId | null>(null)
79
- const [visibleRange, setVisibleRange] = useState({
80
- startIndex: 0,
81
- endIndex: 20,
82
- })
83
-
84
- const { data: allAssets, isLoading: isLoadingAssets } = useAssets()
85
- const { data: chains, isLoading: isLoadingChains } = useChains()
86
-
87
- const chainInfoMap = useMemo(() => {
88
- const map = new Map<ChainId, ChainInfo>()
89
- for (const chain of chains) {
90
- map.set(chain.chainId, chain)
91
- }
92
- return map
93
- }, [chains])
94
-
95
- const allowedAssetChainIds = useMemo(() => {
96
- if (!allowedAssetIds || allowedAssetIds.length === 0) return undefined
97
- const chainIds = new Set<ChainId>()
98
- for (const asset of allAssets) {
99
- if (allowedAssetIds.includes(asset.assetId)) {
100
- chainIds.add(asset.chainId)
101
- }
102
- }
103
- return Array.from(chainIds)
104
- }, [allowedAssetIds, allAssets])
105
-
106
- const filteredChains = useMemo(() => {
107
- let enabledChains = chains.filter(chain => !disabledChainIds.includes(chain.chainId))
108
-
109
- if (allowedChainIds && allowedChainIds.length > 0) {
110
- enabledChains = enabledChains.filter(chain => allowedChainIds.includes(chain.chainId))
111
- }
112
-
113
- if (allowedAssetChainIds) {
114
- enabledChains = enabledChains.filter(chain => allowedAssetChainIds.includes(chain.chainId))
115
- }
116
-
117
- if (!chainSearchQuery.trim()) return enabledChains
118
-
119
- const lowerQuery = chainSearchQuery.toLowerCase()
120
- return enabledChains.filter(chain => chain.name.toLowerCase().includes(lowerQuery))
121
- }, [chains, chainSearchQuery, disabledChainIds, allowedChainIds, allowedAssetChainIds])
122
-
123
- const filteredAssets = useMemo(() => {
124
- let assets = allAssets.filter(
125
- asset =>
126
- !disabledAssetIds.includes(asset.assetId) && !disabledChainIds.includes(asset.chainId),
127
- )
128
-
129
- if (allowedAssetIds && allowedAssetIds.length > 0) {
130
- assets = assets.filter(asset => allowedAssetIds.includes(asset.assetId))
131
- }
132
-
133
- if (allowedChainIds && allowedChainIds.length > 0) {
134
- assets = assets.filter(asset => allowedChainIds.includes(asset.chainId))
135
- }
136
-
137
- if (selectedChainId) {
138
- assets = assets.filter(asset => asset.chainId === selectedChainId)
139
- }
140
-
141
- if (!searchQuery.trim()) {
142
- const natives = assets.filter(a => isNativeAsset(a.assetId))
143
- const others = assets
144
- .filter(a => !isNativeAsset(a.assetId))
145
- .slice(0, Math.max(50 - natives.length, 30))
146
- return [...natives, ...others]
147
- }
148
-
149
- const lowerQuery = searchQuery.toLowerCase()
150
- return assets
151
- .filter(
152
- asset =>
153
- asset.symbol.toLowerCase().includes(lowerQuery) ||
154
- asset.name.toLowerCase().includes(lowerQuery) ||
155
- asset.assetId.toLowerCase().includes(lowerQuery),
156
- )
157
- .map(asset => ({ asset, score: scoreAsset(asset, searchQuery) }))
158
- .sort((a, b) => b.score - a.score)
159
- .slice(0, 100)
160
- .map(item => item.asset)
161
- }, [
162
- allAssets,
163
- selectedChainId,
164
- searchQuery,
165
- disabledAssetIds,
166
- disabledChainIds,
167
- allowedAssetIds,
168
- allowedChainIds,
169
- ])
170
-
171
- const { address: bitcoinAddress } = useBitcoinSigning()
172
- const { address: solanaAddress } = useSolanaSigning()
173
-
174
- const initialAssetPrecisions = useMemo(() => {
175
- const precisions: Record<AssetId, number> = {}
176
- for (const asset of filteredAssets.slice(0, 30)) {
177
- precisions[asset.assetId] = asset.precision
178
- }
179
- return precisions
180
- }, [filteredAssets])
181
-
182
- const initialAssetIds = useMemo(
183
- () => filteredAssets.slice(0, 30).map(a => a.assetId),
184
- [filteredAssets],
185
- )
186
-
187
- const {
188
- data: balances,
189
- loadingAssetIds,
190
- refetchSpecific,
191
- } = useMultiChainBalances(
192
- walletAddress,
193
- bitcoinAddress,
194
- solanaAddress,
195
- initialAssetIds,
196
- initialAssetPrecisions,
197
- )
198
-
199
- useEffect(() => {
200
- if (isOpen && currentAssetIds.length > 0) {
201
- refetchSpecific?.(currentAssetIds)
202
- }
203
- }, [isOpen, currentAssetIds, refetchSpecific])
204
-
205
- const { data: marketData } = useAllMarketData()
206
-
207
- const balanceSortDoneRef = useRef(false)
208
- const sortedAssetsRef = useRef<Asset[]>(filteredAssets)
209
- const prevFilteredRef = useRef(filteredAssets)
210
-
211
- const sortedAssets = useMemo(() => {
212
- const filterChanged = filteredAssets !== prevFilteredRef.current
213
- if (filterChanged) {
214
- prevFilteredRef.current = filteredAssets
215
- balanceSortDoneRef.current = false
216
- }
217
-
218
- if (balanceSortDoneRef.current) {
219
- return sortedAssetsRef.current
220
- }
221
-
222
- if (!balances || Object.keys(balances).length === 0) {
223
- sortedAssetsRef.current = filteredAssets
224
- return filteredAssets
225
- }
226
-
227
- const withBalance: { asset: Asset; fiatValue: number }[] = []
228
- const withoutBalance: Asset[] = []
229
-
230
- for (const asset of filteredAssets) {
231
- const balance = balances[asset.assetId]
232
- if (balance && balance.balance !== '0') {
233
- const price = marketData?.[asset.assetId]?.price
234
- const fiatValue = price
235
- ? bnOrZero(balance.balance)
236
- .div(bnOrZero(10).pow(asset.precision))
237
- .times(bnOrZero(price))
238
- .toNumber()
239
- : 0
240
- withBalance.push({ asset, fiatValue })
241
- } else {
242
- withoutBalance.push(asset)
243
- }
244
- }
245
-
246
- withBalance.sort((a, b) => b.fiatValue - a.fiatValue)
247
-
248
- const result = [...withBalance.map(w => w.asset), ...withoutBalance]
249
- balanceSortDoneRef.current = true
250
- sortedAssetsRef.current = result
251
- return result
252
- }, [filteredAssets, balances, marketData])
253
-
254
- const visibleRangeAssetIds = useMemo(() => {
255
- const start = Math.max(0, visibleRange.startIndex - VISIBLE_BUFFER)
256
- const end = Math.min(sortedAssets.length, visibleRange.endIndex + VISIBLE_BUFFER)
257
- return sortedAssets.slice(start, end).map(a => a.assetId)
258
- }, [sortedAssets, visibleRange])
259
-
260
- useEffect(() => {
261
- if (visibleRangeAssetIds.length > 0) {
262
- refetchSpecific?.(visibleRangeAssetIds)
263
- }
264
- }, [visibleRangeAssetIds, refetchSpecific])
265
-
266
- const handleAssetSelect = useCallback(
267
- (asset: Asset) => {
268
- onSelect(asset)
269
- onClose()
270
- setSearchQuery('')
271
- setSelectedChainId(null)
272
- },
273
- [onSelect, onClose],
274
- )
275
-
276
- const handleChainSelect = useCallback((chainId: ChainId | null) => {
277
- setSelectedChainId(chainId)
278
- }, [])
279
-
280
- const handleBackdropClick = useCallback(
281
- (e: React.MouseEvent) => {
282
- if (e.target === e.currentTarget) {
283
- onClose()
284
- }
285
- },
286
- [onClose],
287
- )
288
-
289
- if (!isOpen) return null
290
-
291
- const isLoading = isLoadingAssets || isLoadingChains
292
-
293
- return (
294
- // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
295
- <div
296
- className='ssw-modal-backdrop'
297
- onClick={handleBackdropClick}
298
- onKeyDown={e => e.key === 'Escape' && onClose()}
299
- role='dialog'
300
- aria-modal='true'
301
- aria-labelledby='token-modal-title'
302
- >
303
- <div className='ssw-modal'>
304
- <div className='ssw-modal-header'>
305
- <h2 id='token-modal-title' className='ssw-modal-title'>
306
- Select Token
307
- </h2>
308
- <button className='ssw-modal-close' onClick={onClose} type='button'>
309
- <svg
310
- width='20'
311
- height='20'
312
- viewBox='0 0 24 24'
313
- fill='none'
314
- stroke='currentColor'
315
- strokeWidth='2'
316
- >
317
- <path d='M18 6L6 18M6 6l12 12' />
318
- </svg>
319
- </button>
320
- </div>
321
-
322
- <div className='ssw-modal-content'>
323
- <div className='ssw-chain-sidebar'>
324
- <div className='ssw-search-wrapper'>
325
- <svg
326
- className='ssw-search-icon'
327
- width='16'
328
- height='16'
329
- viewBox='0 0 24 24'
330
- fill='none'
331
- stroke='currentColor'
332
- strokeWidth='2'
333
- >
334
- <circle cx='11' cy='11' r='8' />
335
- <path d='m21 21-4.35-4.35' />
336
- </svg>
337
- <input
338
- type='text'
339
- className='ssw-chain-search'
340
- placeholder='Search chains'
341
- value={chainSearchQuery}
342
- onChange={e => setChainSearchQuery(e.target.value)}
343
- />
344
- </div>
345
-
346
- <div className='ssw-chain-list'>
347
- <button
348
- className={`ssw-chain-item ${selectedChainId === null ? 'ssw-selected' : ''}`}
349
- onClick={() => handleChainSelect(null)}
350
- type='button'
351
- >
352
- <span className='ssw-chain-name'>All Chains</span>
353
- </button>
354
-
355
- {filteredChains.map(chain => (
356
- <button
357
- key={chain.chainId}
358
- className={`ssw-chain-item ${
359
- selectedChainId === chain.chainId ? 'ssw-selected' : ''
360
- }`}
361
- onClick={() => handleChainSelect(chain.chainId)}
362
- type='button'
363
- >
364
- {chain.icon ? (
365
- <img src={chain.icon} alt={chain.name} className='ssw-chain-icon' />
366
- ) : (
367
- <div
368
- className='ssw-chain-icon-placeholder'
369
- style={{ backgroundColor: chain.color ?? '#888' }}
370
- >
371
- {chain.name.charAt(0)}
372
- </div>
373
- )}
374
- <span className='ssw-chain-name'>{chain.name}</span>
375
- </button>
376
- ))}
377
- </div>
378
- </div>
379
-
380
- <div className='ssw-token-panel'>
381
- <div className='ssw-search-wrapper'>
382
- <svg
383
- className='ssw-search-icon'
384
- width='16'
385
- height='16'
386
- viewBox='0 0 24 24'
387
- fill='none'
388
- stroke='currentColor'
389
- strokeWidth='2'
390
- >
391
- <circle cx='11' cy='11' r='8' />
392
- <path d='m21 21-4.35-4.35' />
393
- </svg>
394
- <input
395
- type='text'
396
- className='ssw-token-search'
397
- placeholder='Search for a token or paste address'
398
- value={searchQuery}
399
- onChange={e => setSearchQuery(e.target.value)}
400
- autoFocus
401
- />
402
- </div>
403
-
404
- <div className='ssw-token-list'>
405
- {isLoading ? (
406
- <div className='ssw-loading'>
407
- <div className='ssw-spinner' />
408
- <span>Loading assets...</span>
409
- </div>
410
- ) : sortedAssets.length === 0 ? (
411
- <div className='ssw-empty'>No tokens found</div>
412
- ) : (
413
- <Virtuoso
414
- data={sortedAssets}
415
- rangeChanged={setVisibleRange}
416
- overscan={200}
417
- itemContent={(_, asset) => {
418
- const chainInfo = chainInfoMap.get(asset.chainId)
419
- const balance = balances?.[asset.assetId]
420
- return (
421
- <button
422
- className='ssw-token-item'
423
- onClick={() => handleAssetSelect(asset)}
424
- type='button'
425
- >
426
- <div className='ssw-token-icon-wrapper'>
427
- {asset.icon ? (
428
- <img src={asset.icon} alt={asset.symbol} className='ssw-token-icon' />
429
- ) : (
430
- <div
431
- className='ssw-token-icon-placeholder'
432
- style={{ backgroundColor: asset.color ?? '#888' }}
433
- >
434
- {asset.symbol?.charAt(0) ?? '?'}
435
- </div>
436
- )}
437
- {chainInfo?.icon && (
438
- <img
439
- src={chainInfo.icon}
440
- alt={chainInfo.name}
441
- className='ssw-token-chain-badge'
442
- />
443
- )}
444
- </div>
445
- <div className='ssw-token-info'>
446
- <span className='ssw-token-symbol'>{asset.symbol}</span>
447
- <span className='ssw-token-name'>
448
- {chainInfo?.name ?? asset.networkName ?? asset.name}
449
- </span>
450
- </div>
451
- <div className='ssw-token-right'>
452
- {(walletAddress || bitcoinAddress || solanaAddress) &&
453
- (loadingAssetIds.has(asset.assetId) ? (
454
- <span className='ssw-token-balance-skeleton' />
455
- ) : balance && balance.balance !== '0' ? (
456
- <>
457
- {marketData?.[asset.assetId]?.price && (
458
- <span className='ssw-token-fiat-value'>
459
- $
460
- {bnOrZero(balance.balance)
461
- .div(bnOrZero(10).pow(asset.precision))
462
- .times(bnOrZero(marketData[asset.assetId].price))
463
- .toNumber()
464
- .toLocaleString(undefined, {
465
- minimumFractionDigits: 2,
466
- maximumFractionDigits: 2,
467
- })}
468
- </span>
469
- )}
470
- <span className='ssw-token-balance'>
471
- {balance.balanceFormatted}
472
- </span>
473
- </>
474
- ) : null)}
475
- </div>
476
- </button>
477
- )
478
- }}
479
- />
480
- )}
481
- </div>
482
- </div>
483
- </div>
484
- </div>
485
- </div>
486
- )
487
- }
@@ -1,92 +0,0 @@
1
- import { useAppKit, useAppKitAccount } from '@reown/appkit/react'
2
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
3
- import type { ReactNode } from 'react'
4
- import { useCallback, useEffect, useMemo, useState } from 'react'
5
- import type { WalletClient } from 'viem'
6
- import type { Config } from 'wagmi'
7
- import { useWalletClient, WagmiProvider } from 'wagmi'
8
-
9
- import { getWagmiAdapter, initializeAppKit } from '../config/appkit'
10
- import { useSwapWallet } from '../contexts/SwapWalletContext'
11
- import { truncateAddress } from '../types'
12
-
13
- const queryClient = new QueryClient()
14
-
15
- type InternalWalletProviderProps = {
16
- projectId: string
17
- children: (walletClient: WalletClient | undefined) => ReactNode
18
- }
19
-
20
- const InternalWalletContent = ({
21
- children,
22
- }: {
23
- children: (walletClient: WalletClient | undefined) => ReactNode
24
- }) => {
25
- const { data: walletClient } = useWalletClient()
26
- return <>{children(walletClient)}</>
27
- }
28
-
29
- export const InternalWalletProvider = ({ projectId, children }: InternalWalletProviderProps) => {
30
- const [isInitialized, setIsInitialized] = useState(false)
31
-
32
- useEffect(() => {
33
- initializeAppKit(projectId)
34
- setIsInitialized(true)
35
- }, [projectId])
36
-
37
- const wagmiConfig = useMemo((): Config | undefined => {
38
- if (!isInitialized) return undefined
39
- const adapter = getWagmiAdapter()
40
- return adapter?.wagmiConfig
41
- }, [isInitialized])
42
-
43
- if (!wagmiConfig) {
44
- return null
45
- }
46
-
47
- return (
48
- <WagmiProvider config={wagmiConfig}>
49
- <QueryClientProvider client={queryClient}>
50
- <InternalWalletContent>{children}</InternalWalletContent>
51
- </QueryClientProvider>
52
- </WagmiProvider>
53
- )
54
- }
55
-
56
- export const ConnectWalletButton = () => {
57
- const { open } = useAppKit()
58
- const { address: appKitAddress, isConnected: appKitConnected } = useAppKitAccount()
59
- const { walletAddress } = useSwapWallet()
60
-
61
- const connectedAddress = walletAddress ?? appKitAddress
62
- const isConnected = !!walletAddress || appKitConnected
63
-
64
- const handleClick = useCallback(() => {
65
- open()
66
- }, [open])
67
-
68
- if (!isConnected) {
69
- return (
70
- <button onClick={handleClick} type='button' className='ssw-connect-btn'>
71
- <svg
72
- width='16'
73
- height='16'
74
- viewBox='0 0 24 24'
75
- fill='none'
76
- stroke='currentColor'
77
- strokeWidth='2'
78
- >
79
- <path d='M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1' />
80
- <path d='M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4' />
81
- </svg>
82
- Connect
83
- </button>
84
- )
85
- }
86
-
87
- return (
88
- <button onClick={handleClick} type='button' className='ssw-connect-btn ssw-connected'>
89
- {connectedAddress ? truncateAddress(connectedAddress) : 'Connected'}
90
- </button>
91
- )
92
- }
@@ -1,104 +0,0 @@
1
- import {
2
- arbitrum,
3
- avalanche,
4
- base,
5
- bitcoin,
6
- bsc,
7
- gnosis,
8
- hyperEvm,
9
- katana,
10
- mainnet,
11
- monad,
12
- optimism,
13
- plasma,
14
- polygon,
15
- solana,
16
- worldchain,
17
- } from '@reown/appkit/networks'
18
- import { createAppKit } from '@reown/appkit/react'
19
- import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin'
20
- import { SolanaAdapter } from '@reown/appkit-adapter-solana/react'
21
- import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
22
- import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets'
23
-
24
- export const EVM_NETWORKS = [
25
- mainnet,
26
- polygon,
27
- arbitrum,
28
- optimism,
29
- base,
30
- avalanche,
31
- bsc,
32
- gnosis,
33
- monad,
34
- hyperEvm,
35
- plasma,
36
- worldchain,
37
- katana,
38
- ] as const
39
-
40
- export const ALL_NETWORKS = [...EVM_NETWORKS, bitcoin, solana] as const
41
-
42
- export type SupportedNetwork = (typeof ALL_NETWORKS)[number]
43
- export type EvmNetwork = (typeof EVM_NETWORKS)[number]
44
-
45
- const APP_METADATA = {
46
- name: 'ShapeShift Swap Widget',
47
- description: 'Multi-chain swap widget powered by ShapeShift',
48
- url: 'https://shapeshift.com',
49
- icons: ['https://shapeshift.com/icon.png'],
50
- }
51
-
52
- let wagmiAdapter: WagmiAdapter | null = null
53
- let bitcoinAdapter: BitcoinAdapter | null = null
54
- let solanaAdapter: SolanaAdapter | null = null
55
- let appKitInitialized = false
56
-
57
- export const createWagmiAdapter = (projectId: string): WagmiAdapter => {
58
- if (!wagmiAdapter) {
59
- wagmiAdapter = new WagmiAdapter({
60
- networks: [...EVM_NETWORKS],
61
- projectId,
62
- })
63
- }
64
- return wagmiAdapter
65
- }
66
-
67
- export const createBitcoinAdapter = (): BitcoinAdapter => {
68
- if (!bitcoinAdapter) {
69
- bitcoinAdapter = new BitcoinAdapter()
70
- }
71
- return bitcoinAdapter
72
- }
73
-
74
- export const createSolanaAdapter = (): SolanaAdapter => {
75
- if (!solanaAdapter) {
76
- solanaAdapter = new SolanaAdapter({
77
- wallets: [new PhantomWalletAdapter(), new SolflareWalletAdapter()] as any,
78
- })
79
- }
80
- return solanaAdapter
81
- }
82
-
83
- export const getWagmiAdapter = (): WagmiAdapter | null => wagmiAdapter
84
-
85
- export const initializeAppKit = (projectId: string): void => {
86
- if (appKitInitialized) {
87
- return
88
- }
89
-
90
- const wagmi = createWagmiAdapter(projectId)
91
- const btc = createBitcoinAdapter()
92
- const sol = createSolanaAdapter()
93
-
94
- createAppKit({
95
- adapters: [wagmi, btc, sol],
96
- projectId,
97
- networks: [...ALL_NETWORKS],
98
- metadata: APP_METADATA,
99
- })
100
-
101
- appKitInitialized = true
102
- }
103
-
104
- export const isAppKitInitialized = (): boolean => appKitInitialized
@@ -1,27 +0,0 @@
1
- import {
2
- arbitrum,
3
- avalanche,
4
- base,
5
- bsc,
6
- gnosis,
7
- mainnet,
8
- optimism,
9
- polygon,
10
- } from '@reown/appkit/networks'
11
- import type { Config } from 'wagmi'
12
-
13
- export const SUPPORTED_CHAINS = [
14
- mainnet,
15
- polygon,
16
- arbitrum,
17
- optimism,
18
- base,
19
- avalanche,
20
- bsc,
21
- gnosis,
22
- ] as const
23
-
24
- export type SupportedChains = typeof SUPPORTED_CHAINS
25
- export type SupportedChainId = SupportedChains[number]['id']
26
-
27
- export type WagmiConfig = Config