@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.
- package/LICENSE.md +21 -0
- package/README.md +18 -11
- package/package.json +77 -28
- package/Dockerfile +0 -87
- package/dist/assets/PhArrowCircleDown-B305t-Sh.js +0 -14
- package/dist/assets/PhArrowClockwise-CJPxQhr_.js +0 -14
- package/dist/assets/PhArrowDown-C4hT9BVu.js +0 -14
- package/dist/assets/PhArrowLeft-Way4Cu-r.js +0 -14
- package/dist/assets/PhArrowRight-Cp5ZME0F.js +0 -14
- package/dist/assets/PhArrowSquareOut-NnrK69c5.js +0 -14
- package/dist/assets/PhArrowUp-Dph6j8rm.js +0 -14
- package/dist/assets/PhArrowUpRight-BpJjRRRh.js +0 -14
- package/dist/assets/PhArrowsClockwise-CFUT8X7R.js +0 -14
- package/dist/assets/PhArrowsDownUp-BjBI4ckC.js +0 -14
- package/dist/assets/PhArrowsLeftRight-CuLpbKRk.js +0 -14
- package/dist/assets/PhBank-Dchr4XQJ.js +0 -14
- package/dist/assets/PhBrowser-dUu2TrC1.js +0 -14
- package/dist/assets/PhCaretDown-78IuKU3V.js +0 -14
- package/dist/assets/PhCaretLeft-_aiyH7n1.js +0 -14
- package/dist/assets/PhCaretRight-8zJQEuDX.js +0 -14
- package/dist/assets/PhCaretUp-CiCxKWwN.js +0 -14
- package/dist/assets/PhCheck-CcADx8br.js +0 -14
- package/dist/assets/PhCircleHalf-BYzSzd2h.js +0 -14
- package/dist/assets/PhClock-UItiXeJq.js +0 -14
- package/dist/assets/PhCompass-CUy3uA7t.js +0 -14
- package/dist/assets/PhCopy-C-PDgSlZ.js +0 -14
- package/dist/assets/PhCreditCard-CWRaUT6F.js +0 -14
- package/dist/assets/PhCurrencyDollar-Cqla3AXT.js +0 -14
- package/dist/assets/PhDesktop-BBIPrYLl.js +0 -14
- package/dist/assets/PhDeviceMobile-BdsPaATg.js +0 -14
- package/dist/assets/PhDotsThree-BMXCQl-0.js +0 -14
- package/dist/assets/PhEnvelope-yUsis6yU.js +0 -14
- package/dist/assets/PhFunnelSimple-Ba7BbWTp.js +0 -14
- package/dist/assets/PhGlobe-DgLxBk5U.js +0 -14
- package/dist/assets/PhIdentificationCard-Czjmq9Z3.js +0 -14
- package/dist/assets/PhImage-BAJ_erz6.js +0 -14
- package/dist/assets/PhInfo-Cr4DhVUE.js +0 -14
- package/dist/assets/PhLightbulb-BX_7duTr.js +0 -14
- package/dist/assets/PhMagnifyingGlass-Mygfryvz.js +0 -14
- package/dist/assets/PhPaperPlaneRight-BeuCA660.js +0 -14
- package/dist/assets/PhPlus-BMjcHjsT.js +0 -14
- package/dist/assets/PhPower-CGffGmdb.js +0 -14
- package/dist/assets/PhPuzzlePiece-wm_GpVSi.js +0 -14
- package/dist/assets/PhQrCode-DRwRzXCH.js +0 -14
- package/dist/assets/PhQuestion-M09j2U0T.js +0 -14
- package/dist/assets/PhQuestionMark-BP9EMB6h.js +0 -14
- package/dist/assets/PhSealCheck-BfG80yKn.js +0 -14
- package/dist/assets/PhSignOut-C5YOWlPT.js +0 -14
- package/dist/assets/PhSpinner-BXooDYzU.js +0 -14
- package/dist/assets/PhTrash-Biq6rmmL.js +0 -14
- package/dist/assets/PhUser-CAgZSpGe.js +0 -14
- package/dist/assets/PhVault-l2PHF38a.js +0 -14
- package/dist/assets/PhWallet-w25D16It.js +0 -14
- package/dist/assets/PhWarning-DPEydIPw.js +0 -14
- package/dist/assets/PhWarningCircle-BqjT745H.js +0 -14
- package/dist/assets/PhX-s-SmVExa.js +0 -14
- package/dist/assets/SwapController-If0bFrNc.js +0 -1
- package/dist/assets/ccip-BBB7uCdL.js +0 -1
- package/dist/assets/ccip-CkkdfKKI.js +0 -1
- package/dist/assets/data-capture-D4gAy-Yc.js +0 -242
- package/dist/assets/email-B1AJehis.js +0 -90
- package/dist/assets/embedded-wallet-Q6DQopDk.js +0 -410
- package/dist/assets/features-B6JAPAFb.js +0 -5
- package/dist/assets/index-5jd8RPN9.js +0 -7428
- package/dist/assets/index-B4kNtSTk.js +0 -1080
- package/dist/assets/index-B753eAob.js +0 -1
- package/dist/assets/index-BZoLTv77.js +0 -3
- package/dist/assets/index-Bcw9UURX.js +0 -3
- package/dist/assets/index-BkrV-ahK.js +0 -2
- package/dist/assets/index-CRQzQ_up.js +0 -9
- package/dist/assets/index-CSW0Gt5y.js +0 -132
- package/dist/assets/index-CZ4jTIsk.js +0 -138
- package/dist/assets/index-Cwa8SB3h.js +0 -61
- package/dist/assets/index-DEwQgT4r.js +0 -78
- package/dist/assets/index-DVfQiQ1d.js +0 -1
- package/dist/assets/index-cV3UdaMs.css +0 -1
- package/dist/assets/index-hAmu622O.js +0 -43
- package/dist/assets/onramp-XcPNCLRU.js +0 -422
- package/dist/assets/pay-with-exchange-BeHt4OF4.js +0 -280
- package/dist/assets/property-C9JG5tag.js +0 -27
- package/dist/assets/receive-7ff4he2S.js +0 -131
- package/dist/assets/send-CkMBIRXT.js +0 -554
- package/dist/assets/socials-DmmxuLrP.js +0 -250
- package/dist/assets/swaps-IHAYRQ0x.js +0 -1050
- package/dist/assets/transactions-9Cxdk7o9.js +0 -16
- package/dist/assets/v4-BOvFkHkt.js +0 -1
- package/dist/assets/w3m-modal-kJzFnBfB.js +0 -891
- package/dist/assets/wallet-standard-Dv8LMoUB.js +0 -1
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +0 -11
- package/dist/icon-192x192.png +0 -0
- package/dist/icon-512x512.png +0 -0
- package/dist/index.html +0 -33
- package/index.html +0 -32
- package/public/favicon.png +0 -0
- package/public/favicon.svg +0 -11
- package/public/icon-192x192.png +0 -0
- package/public/icon-512x512.png +0 -0
- package/railway.toml +0 -10
- package/src/api/client.ts +0 -95
- package/src/components/AddressInputModal.css +0 -173
- package/src/components/AddressInputModal.tsx +0 -250
- package/src/components/ApprovalStep.tsx +0 -92
- package/src/components/ExecutionStep.tsx +0 -27
- package/src/components/InputStep.tsx +0 -431
- package/src/components/QuoteSelector.css +0 -146
- package/src/components/QuoteSelector.tsx +0 -137
- package/src/components/QuotesModal.css +0 -250
- package/src/components/QuotesModal.tsx +0 -182
- package/src/components/SettingsModal.css +0 -145
- package/src/components/SettingsModal.tsx +0 -180
- package/src/components/StatusStep.tsx +0 -139
- package/src/components/SwapWidget.css +0 -713
- package/src/components/SwapWidget.tsx +0 -607
- package/src/components/TokenSelectModal.css +0 -438
- package/src/components/TokenSelectModal.tsx +0 -487
- package/src/components/WalletProvider.tsx +0 -92
- package/src/config/appkit.ts +0 -104
- package/src/config/wagmi.ts +0 -27
- package/src/constants/chains.ts +0 -72
- package/src/constants/defaults.ts +0 -28
- package/src/constants/swappers.ts +0 -48
- package/src/constants/viemChains.ts +0 -69
- package/src/contexts/SwapWalletContext.tsx +0 -25
- package/src/demo/App.css +0 -458
- package/src/demo/App.tsx +0 -481
- package/src/demo/main.tsx +0 -25
- package/src/hooks/useAssets.ts +0 -180
- package/src/hooks/useBalances.ts +0 -822
- package/src/hooks/useBitcoinSigning.ts +0 -276
- package/src/hooks/useMarketData.ts +0 -169
- package/src/hooks/useSolanaSigning.ts +0 -275
- package/src/hooks/useStatusPolling.ts +0 -137
- package/src/hooks/useSwapApproval.ts +0 -106
- package/src/hooks/useSwapDisplayValues.ts +0 -157
- package/src/hooks/useSwapExecution.ts +0 -199
- package/src/hooks/useSwapHandlers.ts +0 -119
- package/src/hooks/useSwapQuote.ts +0 -71
- package/src/hooks/useSwapQuoting.ts +0 -102
- package/src/hooks/useSwapRates.ts +0 -61
- package/src/index.ts +0 -43
- package/src/machines/SwapMachineContext.ts +0 -5
- package/src/machines/__tests__/guards.test.ts +0 -228
- package/src/machines/__tests__/setup.test.ts +0 -7
- package/src/machines/__tests__/swapMachine.test.ts +0 -544
- package/src/machines/__tests__/types.test.ts +0 -124
- package/src/machines/guards.ts +0 -33
- package/src/machines/swapMachine.ts +0 -288
- package/src/machines/types.ts +0 -54
- package/src/services/transactionStatus.ts +0 -322
- package/src/types/index.ts +0 -361
- package/src/utils/addressValidation.ts +0 -201
- package/src/utils/redirect.ts +0 -45
- package/src/vite-env.d.ts +0 -10
- package/tsconfig.json +0 -23
- package/tsconfig.node.json +0 -10
- package/vite.config.ts +0 -101
- 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
|
-
}
|
package/src/config/appkit.ts
DELETED
|
@@ -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
|
package/src/config/wagmi.ts
DELETED
|
@@ -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
|