@shapeshiftoss/swap-widget 0.0.2 → 0.1.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 (159) hide show
  1. package/dist/index.d.ts +233 -0
  2. package/dist/index.js +63612 -0
  3. package/dist/style.css +1 -0
  4. package/package.json +69 -18
  5. package/Dockerfile +0 -87
  6. package/dist/assets/PhArrowCircleDown-B305t-Sh.js +0 -14
  7. package/dist/assets/PhArrowClockwise-CJPxQhr_.js +0 -14
  8. package/dist/assets/PhArrowDown-C4hT9BVu.js +0 -14
  9. package/dist/assets/PhArrowLeft-Way4Cu-r.js +0 -14
  10. package/dist/assets/PhArrowRight-Cp5ZME0F.js +0 -14
  11. package/dist/assets/PhArrowSquareOut-NnrK69c5.js +0 -14
  12. package/dist/assets/PhArrowUp-Dph6j8rm.js +0 -14
  13. package/dist/assets/PhArrowUpRight-BpJjRRRh.js +0 -14
  14. package/dist/assets/PhArrowsClockwise-CFUT8X7R.js +0 -14
  15. package/dist/assets/PhArrowsDownUp-BjBI4ckC.js +0 -14
  16. package/dist/assets/PhArrowsLeftRight-CuLpbKRk.js +0 -14
  17. package/dist/assets/PhBank-Dchr4XQJ.js +0 -14
  18. package/dist/assets/PhBrowser-dUu2TrC1.js +0 -14
  19. package/dist/assets/PhCaretDown-78IuKU3V.js +0 -14
  20. package/dist/assets/PhCaretLeft-_aiyH7n1.js +0 -14
  21. package/dist/assets/PhCaretRight-8zJQEuDX.js +0 -14
  22. package/dist/assets/PhCaretUp-CiCxKWwN.js +0 -14
  23. package/dist/assets/PhCheck-CcADx8br.js +0 -14
  24. package/dist/assets/PhCircleHalf-BYzSzd2h.js +0 -14
  25. package/dist/assets/PhClock-UItiXeJq.js +0 -14
  26. package/dist/assets/PhCompass-CUy3uA7t.js +0 -14
  27. package/dist/assets/PhCopy-C-PDgSlZ.js +0 -14
  28. package/dist/assets/PhCreditCard-CWRaUT6F.js +0 -14
  29. package/dist/assets/PhCurrencyDollar-Cqla3AXT.js +0 -14
  30. package/dist/assets/PhDesktop-BBIPrYLl.js +0 -14
  31. package/dist/assets/PhDeviceMobile-BdsPaATg.js +0 -14
  32. package/dist/assets/PhDotsThree-BMXCQl-0.js +0 -14
  33. package/dist/assets/PhEnvelope-yUsis6yU.js +0 -14
  34. package/dist/assets/PhFunnelSimple-Ba7BbWTp.js +0 -14
  35. package/dist/assets/PhGlobe-DgLxBk5U.js +0 -14
  36. package/dist/assets/PhIdentificationCard-Czjmq9Z3.js +0 -14
  37. package/dist/assets/PhImage-BAJ_erz6.js +0 -14
  38. package/dist/assets/PhInfo-Cr4DhVUE.js +0 -14
  39. package/dist/assets/PhLightbulb-BX_7duTr.js +0 -14
  40. package/dist/assets/PhMagnifyingGlass-Mygfryvz.js +0 -14
  41. package/dist/assets/PhPaperPlaneRight-BeuCA660.js +0 -14
  42. package/dist/assets/PhPlus-BMjcHjsT.js +0 -14
  43. package/dist/assets/PhPower-CGffGmdb.js +0 -14
  44. package/dist/assets/PhPuzzlePiece-wm_GpVSi.js +0 -14
  45. package/dist/assets/PhQrCode-DRwRzXCH.js +0 -14
  46. package/dist/assets/PhQuestion-M09j2U0T.js +0 -14
  47. package/dist/assets/PhQuestionMark-BP9EMB6h.js +0 -14
  48. package/dist/assets/PhSealCheck-BfG80yKn.js +0 -14
  49. package/dist/assets/PhSignOut-C5YOWlPT.js +0 -14
  50. package/dist/assets/PhSpinner-BXooDYzU.js +0 -14
  51. package/dist/assets/PhTrash-Biq6rmmL.js +0 -14
  52. package/dist/assets/PhUser-CAgZSpGe.js +0 -14
  53. package/dist/assets/PhVault-l2PHF38a.js +0 -14
  54. package/dist/assets/PhWallet-w25D16It.js +0 -14
  55. package/dist/assets/PhWarning-DPEydIPw.js +0 -14
  56. package/dist/assets/PhWarningCircle-BqjT745H.js +0 -14
  57. package/dist/assets/PhX-s-SmVExa.js +0 -14
  58. package/dist/assets/SwapController-If0bFrNc.js +0 -1
  59. package/dist/assets/ccip-BBB7uCdL.js +0 -1
  60. package/dist/assets/ccip-CkkdfKKI.js +0 -1
  61. package/dist/assets/data-capture-D4gAy-Yc.js +0 -242
  62. package/dist/assets/email-B1AJehis.js +0 -90
  63. package/dist/assets/embedded-wallet-Q6DQopDk.js +0 -410
  64. package/dist/assets/features-B6JAPAFb.js +0 -5
  65. package/dist/assets/index-5jd8RPN9.js +0 -7428
  66. package/dist/assets/index-B4kNtSTk.js +0 -1080
  67. package/dist/assets/index-B753eAob.js +0 -1
  68. package/dist/assets/index-BZoLTv77.js +0 -3
  69. package/dist/assets/index-Bcw9UURX.js +0 -3
  70. package/dist/assets/index-BkrV-ahK.js +0 -2
  71. package/dist/assets/index-CRQzQ_up.js +0 -9
  72. package/dist/assets/index-CSW0Gt5y.js +0 -132
  73. package/dist/assets/index-CZ4jTIsk.js +0 -138
  74. package/dist/assets/index-Cwa8SB3h.js +0 -61
  75. package/dist/assets/index-DEwQgT4r.js +0 -78
  76. package/dist/assets/index-DVfQiQ1d.js +0 -1
  77. package/dist/assets/index-cV3UdaMs.css +0 -1
  78. package/dist/assets/index-hAmu622O.js +0 -43
  79. package/dist/assets/onramp-XcPNCLRU.js +0 -422
  80. package/dist/assets/pay-with-exchange-BeHt4OF4.js +0 -280
  81. package/dist/assets/property-C9JG5tag.js +0 -27
  82. package/dist/assets/receive-7ff4he2S.js +0 -131
  83. package/dist/assets/send-CkMBIRXT.js +0 -554
  84. package/dist/assets/socials-DmmxuLrP.js +0 -250
  85. package/dist/assets/swaps-IHAYRQ0x.js +0 -1050
  86. package/dist/assets/transactions-9Cxdk7o9.js +0 -16
  87. package/dist/assets/v4-BOvFkHkt.js +0 -1
  88. package/dist/assets/w3m-modal-kJzFnBfB.js +0 -891
  89. package/dist/assets/wallet-standard-Dv8LMoUB.js +0 -1
  90. package/dist/favicon.png +0 -0
  91. package/dist/favicon.svg +0 -11
  92. package/dist/icon-192x192.png +0 -0
  93. package/dist/icon-512x512.png +0 -0
  94. package/dist/index.html +0 -33
  95. package/index.html +0 -32
  96. package/public/favicon.png +0 -0
  97. package/public/favicon.svg +0 -11
  98. package/public/icon-192x192.png +0 -0
  99. package/public/icon-512x512.png +0 -0
  100. package/railway.toml +0 -10
  101. package/src/api/client.ts +0 -95
  102. package/src/components/AddressInputModal.css +0 -173
  103. package/src/components/AddressInputModal.tsx +0 -250
  104. package/src/components/ApprovalStep.tsx +0 -92
  105. package/src/components/ExecutionStep.tsx +0 -27
  106. package/src/components/InputStep.tsx +0 -431
  107. package/src/components/QuoteSelector.css +0 -146
  108. package/src/components/QuoteSelector.tsx +0 -137
  109. package/src/components/QuotesModal.css +0 -250
  110. package/src/components/QuotesModal.tsx +0 -182
  111. package/src/components/SettingsModal.css +0 -145
  112. package/src/components/SettingsModal.tsx +0 -180
  113. package/src/components/StatusStep.tsx +0 -139
  114. package/src/components/SwapWidget.css +0 -713
  115. package/src/components/SwapWidget.tsx +0 -607
  116. package/src/components/TokenSelectModal.css +0 -438
  117. package/src/components/TokenSelectModal.tsx +0 -487
  118. package/src/components/WalletProvider.tsx +0 -92
  119. package/src/config/appkit.ts +0 -104
  120. package/src/config/wagmi.ts +0 -27
  121. package/src/constants/chains.ts +0 -72
  122. package/src/constants/defaults.ts +0 -28
  123. package/src/constants/swappers.ts +0 -48
  124. package/src/constants/viemChains.ts +0 -69
  125. package/src/contexts/SwapWalletContext.tsx +0 -25
  126. package/src/demo/App.css +0 -458
  127. package/src/demo/App.tsx +0 -481
  128. package/src/demo/main.tsx +0 -25
  129. package/src/hooks/useAssets.ts +0 -180
  130. package/src/hooks/useBalances.ts +0 -822
  131. package/src/hooks/useBitcoinSigning.ts +0 -276
  132. package/src/hooks/useMarketData.ts +0 -169
  133. package/src/hooks/useSolanaSigning.ts +0 -275
  134. package/src/hooks/useStatusPolling.ts +0 -137
  135. package/src/hooks/useSwapApproval.ts +0 -106
  136. package/src/hooks/useSwapDisplayValues.ts +0 -157
  137. package/src/hooks/useSwapExecution.ts +0 -199
  138. package/src/hooks/useSwapHandlers.ts +0 -119
  139. package/src/hooks/useSwapQuote.ts +0 -71
  140. package/src/hooks/useSwapQuoting.ts +0 -102
  141. package/src/hooks/useSwapRates.ts +0 -61
  142. package/src/index.ts +0 -43
  143. package/src/machines/SwapMachineContext.ts +0 -5
  144. package/src/machines/__tests__/guards.test.ts +0 -228
  145. package/src/machines/__tests__/setup.test.ts +0 -7
  146. package/src/machines/__tests__/swapMachine.test.ts +0 -544
  147. package/src/machines/__tests__/types.test.ts +0 -124
  148. package/src/machines/guards.ts +0 -33
  149. package/src/machines/swapMachine.ts +0 -288
  150. package/src/machines/types.ts +0 -54
  151. package/src/services/transactionStatus.ts +0 -322
  152. package/src/types/index.ts +0 -361
  153. package/src/utils/addressValidation.ts +0 -201
  154. package/src/utils/redirect.ts +0 -45
  155. package/src/vite-env.d.ts +0 -10
  156. package/tsconfig.json +0 -23
  157. package/tsconfig.node.json +0 -10
  158. package/vite.config.ts +0 -101
  159. package/vitest.config.ts +0 -18
@@ -1,607 +0,0 @@
1
- import './SwapWidget.css'
2
-
3
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
4
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
5
- import type { WalletClient } from 'viem'
6
-
7
- import { createApiClient } from '../api/client'
8
- import { DEFAULT_BUY_ASSET, DEFAULT_SELL_ASSET } from '../constants/defaults'
9
- import type { SwapWalletContextValue } from '../contexts/SwapWalletContext'
10
- import { SwapWalletProvider, useSwapWallet } from '../contexts/SwapWalletContext'
11
- import { useBitcoinSigning } from '../hooks/useBitcoinSigning'
12
- import { useSolanaSigning } from '../hooks/useSolanaSigning'
13
- import { useStatusPolling } from '../hooks/useStatusPolling'
14
- import { useSwapApproval } from '../hooks/useSwapApproval'
15
- import { useSwapDisplayValues } from '../hooks/useSwapDisplayValues'
16
- import { useSwapExecution } from '../hooks/useSwapExecution'
17
- import { useSwapHandlers } from '../hooks/useSwapHandlers'
18
- import { useSwapQuoting } from '../hooks/useSwapQuoting'
19
- import { SwapMachineCtx } from '../machines/SwapMachineContext'
20
- import type { Asset, SwapWidgetProps, ThemeMode } from '../types'
21
- import { getChainType } from '../types'
22
- import { AddressInputModal } from './AddressInputModal'
23
- import { ApprovalStep } from './ApprovalStep'
24
- import { ExecutionStep } from './ExecutionStep'
25
- import { InputStep } from './InputStep'
26
- import { SettingsModal } from './SettingsModal'
27
- import { StatusStep } from './StatusStep'
28
- import { TokenSelectModal } from './TokenSelectModal'
29
- import { ConnectWalletButton, InternalWalletProvider } from './WalletProvider'
30
-
31
- const queryClient = new QueryClient({
32
- defaultOptions: {
33
- queries: {
34
- refetchOnWindowFocus: false,
35
- staleTime: 30_000,
36
- },
37
- },
38
- })
39
-
40
- type SwapWidgetContentProps = {
41
- apiClient: ReturnType<typeof createApiClient>
42
- theme: SwapWidgetProps['theme']
43
- showPoweredBy: boolean
44
- defaultReceiveAddress?: string
45
- enableWalletConnection: boolean
46
- isBuyAssetLocked: boolean
47
- onConnectWallet?: () => void
48
- onSwapSuccess?: (txHash: string) => void
49
- onSwapError?: (error: Error) => void
50
- onAssetSelect?: (type: 'sell' | 'buy', asset: Asset) => void
51
- sellDisabledAssetIds: string[]
52
- buyDisabledAssetIds: string[]
53
- sellDisabledChainIds: string[]
54
- buyDisabledChainIds: string[]
55
- sellAllowedChainIds?: string[]
56
- buyAllowedChainIds?: string[]
57
- sellAllowedAssetIds?: string[]
58
- buyAllowedAssetIds?: string[]
59
- }
60
-
61
- const SwapWidgetContent = ({
62
- apiClient,
63
- theme = 'dark',
64
- showPoweredBy,
65
- defaultReceiveAddress,
66
- enableWalletConnection,
67
- isBuyAssetLocked,
68
- onConnectWallet,
69
- onSwapSuccess,
70
- onSwapError,
71
- onAssetSelect,
72
- sellDisabledAssetIds,
73
- buyDisabledAssetIds,
74
- sellDisabledChainIds,
75
- buyDisabledChainIds,
76
- sellAllowedChainIds,
77
- buyAllowedChainIds,
78
- sellAllowedAssetIds,
79
- buyAllowedAssetIds,
80
- }: SwapWidgetContentProps) => {
81
- const state = SwapMachineCtx.useSelector(s => s)
82
- const actorRef = SwapMachineCtx.useActorRef()
83
-
84
- const {
85
- walletAddress,
86
- effectiveReceiveAddress,
87
- isCustomAddress,
88
- customReceiveAddress,
89
- setCustomReceiveAddress,
90
- bitcoin,
91
- solana,
92
- } = useSwapWallet()
93
-
94
- const [tokenModalType, setTokenModalType] = useState<'sell' | 'buy' | null>(null)
95
- const [isSettingsOpen, setIsSettingsOpen] = useState(false)
96
- const [isAddressModalOpen, setIsAddressModalOpen] = useState(false)
97
-
98
- const themeMode: ThemeMode = typeof theme === 'string' ? theme : theme.mode
99
- const themeConfig = typeof theme === 'object' ? theme : undefined
100
-
101
- const buyChainType = getChainType(state.context.buyAsset.chainId)
102
-
103
- const {
104
- rates,
105
- isLoadingRates,
106
- ratesError,
107
- sellAssetBalance,
108
- isSellBalanceLoading,
109
- refetchSellBalance,
110
- buyAssetBalance,
111
- isBuyBalanceLoading,
112
- refetchBuyBalance,
113
- sellChainInfo,
114
- buyChainInfo,
115
- displayRate,
116
- networkFeeDisplay,
117
- sellUsdValue,
118
- buyUsdValue,
119
- buyAssetUsdPrice,
120
- sellBalanceFiatValue,
121
- buyBalanceFiatValue,
122
- } = useSwapDisplayValues({ apiClient })
123
-
124
- const {
125
- handleSwapTokens,
126
- handleSellAssetSelect,
127
- handleBuyAssetSelect,
128
- handleSellAmountChange,
129
- handleSelectRate,
130
- handleSlippageChange,
131
- handleButtonClick,
132
- } = useSwapHandlers({ onConnectWallet, onAssetSelect })
133
-
134
- useSwapQuoting({ apiClient, rates, sellAssetBalance })
135
-
136
- useSwapApproval()
137
-
138
- useSwapExecution()
139
-
140
- useStatusPolling({ onSwapSuccess, onSwapError, refetchSellBalance, refetchBuyBalance })
141
-
142
- const widgetStyle = useMemo(() => {
143
- if (!themeConfig) return undefined
144
- const style: Record<string, string> = {}
145
- if (themeConfig.accentColor) {
146
- style['--ssw-accent'] = themeConfig.accentColor
147
- style['--ssw-accent-light'] = `${themeConfig.accentColor}1a`
148
- }
149
- if (themeConfig.backgroundColor) {
150
- style['--ssw-bg-secondary'] = themeConfig.backgroundColor
151
- style['--ssw-bg-primary'] = themeConfig.backgroundColor
152
- }
153
- if (themeConfig.cardColor) {
154
- style['--ssw-bg-tertiary'] = themeConfig.cardColor
155
- }
156
- if (themeConfig.textColor) {
157
- style['--ssw-text-primary'] = themeConfig.textColor
158
- }
159
- if (themeConfig.borderRadius) {
160
- const base = parseFloat(themeConfig.borderRadius) || 0
161
- style['--ssw-radius-widget'] = `${base * 1.25}px`
162
- style['--ssw-radius-lg'] = `${base}px`
163
- style['--ssw-radius-md'] = `${Math.round(base * 0.875)}px`
164
- style['--ssw-radius-sm'] = `${Math.round(base * 0.625)}px`
165
- style['--ssw-radius-xs'] = `${Math.round(base * 0.375)}px`
166
- style['--ssw-radius-2xs'] = `${Math.round(base * 0.25)}px`
167
- }
168
- if (themeConfig.fontFamily) {
169
- style['--ssw-font-family'] = themeConfig.fontFamily
170
- }
171
- if (themeConfig.borderColor) {
172
- style['--ssw-border'] = themeConfig.borderColor
173
- style['--ssw-border-hover'] = themeConfig.borderColor
174
- }
175
- if (themeConfig.secondaryTextColor) {
176
- style['--ssw-text-secondary'] = themeConfig.secondaryTextColor
177
- }
178
- if (themeConfig.mutedTextColor) {
179
- style['--ssw-text-muted'] = themeConfig.mutedTextColor
180
- }
181
- if (themeConfig.inputColor) {
182
- style['--ssw-bg-input'] = themeConfig.inputColor
183
- }
184
- if (themeConfig.hoverColor) {
185
- style['--ssw-bg-hover'] = themeConfig.hoverColor
186
- }
187
- return Object.keys(style).length > 0 ? (style as React.CSSProperties) : undefined
188
- }, [themeConfig])
189
-
190
- const openAddressModal = useCallback(() => setIsAddressModalOpen(true), [])
191
-
192
- return (
193
- <div
194
- className={`ssw-widget ${themeMode === 'light' ? 'ssw-light' : 'ssw-dark'}${
195
- themeConfig?.buttonVariant === 'outline' ? ' ssw-btn-outline' : ''
196
- }`}
197
- style={widgetStyle}
198
- >
199
- <div className='ssw-header'>
200
- <span className='ssw-header-title'>Swap</span>
201
- <div className='ssw-header-actions'>
202
- {enableWalletConnection && <ConnectWalletButton />}
203
- <button
204
- className='ssw-settings-btn'
205
- onClick={() => setIsSettingsOpen(true)}
206
- type='button'
207
- title='Settings'
208
- >
209
- <svg
210
- width='20'
211
- height='20'
212
- viewBox='0 0 24 24'
213
- fill='none'
214
- stroke='currentColor'
215
- strokeWidth='2'
216
- >
217
- <circle cx='12' cy='12' r='3' />
218
- <path d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z' />
219
- </svg>
220
- </button>
221
- </div>
222
- </div>
223
-
224
- <div className='ssw-step-container'>
225
- {(state.matches('idle') || state.matches('input') || state.matches('quoting')) && (
226
- <InputStep
227
- context={state.context}
228
- send={actorRef.send}
229
- rates={rates ?? []}
230
- isLoadingRates={isLoadingRates}
231
- ratesError={ratesError}
232
- sellAssetBalance={sellAssetBalance}
233
- buyAssetBalance={buyAssetBalance}
234
- isSellBalanceLoading={isSellBalanceLoading}
235
- isBuyBalanceLoading={isBuyBalanceLoading}
236
- sellUsdValue={sellUsdValue}
237
- buyUsdValue={buyUsdValue}
238
- sellChainInfo={sellChainInfo}
239
- buyChainInfo={buyChainInfo}
240
- displayRate={displayRate}
241
- walletAddress={walletAddress}
242
- bitcoinAddress={bitcoin.address}
243
- solanaAddress={solana.address}
244
- defaultReceiveAddress={defaultReceiveAddress}
245
- buyAssetUsdPrice={buyAssetUsdPrice}
246
- onOpenTokenModal={setTokenModalType}
247
- onOpenAddressModal={openAddressModal}
248
- enableWalletConnection={enableWalletConnection}
249
- onConnectWallet={onConnectWallet}
250
- bitcoinState={bitcoin.state}
251
- solanaState={solana.state}
252
- isQuoting={state.matches('quoting')}
253
- isExecuting={false}
254
- effectiveReceiveAddress={effectiveReceiveAddress}
255
- isCustomAddress={isCustomAddress}
256
- sellAmount={state.context.sellAmount}
257
- onSellAmountChange={handleSellAmountChange}
258
- onSwapTokens={handleSwapTokens}
259
- onSelectRate={handleSelectRate}
260
- onButtonClick={handleButtonClick}
261
- sellAmountBaseUnit={state.context.sellAmountBaseUnit}
262
- networkFeeDisplay={networkFeeDisplay}
263
- sellBalanceFiatValue={sellBalanceFiatValue}
264
- buyBalanceFiatValue={buyBalanceFiatValue}
265
- isBuyAssetLocked={isBuyAssetLocked}
266
- />
267
- )}
268
-
269
- {(state.matches('approval_needed') || state.matches('approving')) && (
270
- <ApprovalStep
271
- context={state.context}
272
- send={actorRef.send}
273
- isApproving={state.matches('approving')}
274
- />
275
- )}
276
-
277
- {state.matches('executing') && (
278
- <ExecutionStep context={state.context} send={actorRef.send} />
279
- )}
280
-
281
- {(state.matches('polling_status') ||
282
- state.matches('complete') ||
283
- state.matches('error')) && (
284
- <StatusStep
285
- context={state.context}
286
- send={actorRef.send}
287
- isPolling={state.matches('polling_status')}
288
- isComplete={state.matches('complete')}
289
- isError={state.matches('error')}
290
- />
291
- )}
292
- </div>
293
-
294
- {showPoweredBy && (
295
- <div className='ssw-powered-by'>
296
- Powered by{' '}
297
- <a
298
- href='https://shapeshift.com'
299
- target='_blank'
300
- rel='noopener noreferrer'
301
- className='ssw-powered-by-link'
302
- >
303
- <svg width='16' height='16' viewBox='0 0 57 62' fill='currentColor'>
304
- <path d='M51.67 5.1L48.97 21.3L39.37 10L51.67 5.1ZM49.03 28.27L51.43 37.14L33.06 42.2L49.03 28.27ZM9.03 23.8L18.88 10.93H35.99L46.92 23.8H9.03ZM45.66 26.99L27.85 42.53L9.7 26.99H45.66ZM15.58 10.01L6.78 21.51L4.08 5.17L15.58 10.01ZM22.57 42.2L4.02 37.15L6.56 28.48L22.57 42.2ZM25.99 46.43L22.49 50.28C19.53 47.46 16.26 44.96 12.78 42.83L25.99 46.43ZM42.98 42.77C39.5 44.94 36.24 47.47 33.29 50.32L29.72 46.42L42.98 42.77ZM55.73 0.06L36.42 7.75H18.42L0 0L4.18 25.3L0.17 38.99L10.65 45.26C15.61 48.23 20.06 51.94 23.86 56.3L27.94 60.97L32.23 56.06C35.9 51.84 40.18 48.22 44.95 45.29L55.23 38.99L51.52 25.31L55.73 0.06Z' />
305
- </svg>
306
- ShapeShift
307
- </a>
308
- </div>
309
- )}
310
-
311
- <TokenSelectModal
312
- isOpen={tokenModalType !== null}
313
- onClose={() => setTokenModalType(null)}
314
- onSelect={tokenModalType === 'sell' ? handleSellAssetSelect : handleBuyAssetSelect}
315
- disabledAssetIds={tokenModalType === 'buy' ? buyDisabledAssetIds : sellDisabledAssetIds}
316
- disabledChainIds={tokenModalType === 'buy' ? buyDisabledChainIds : sellDisabledChainIds}
317
- allowedChainIds={tokenModalType === 'buy' ? buyAllowedChainIds : sellAllowedChainIds}
318
- allowedAssetIds={tokenModalType === 'buy' ? buyAllowedAssetIds : sellAllowedAssetIds}
319
- walletAddress={walletAddress}
320
- currentAssetIds={[state.context.sellAsset.assetId, state.context.buyAsset.assetId]}
321
- />
322
-
323
- <SettingsModal
324
- isOpen={isSettingsOpen}
325
- onClose={() => setIsSettingsOpen(false)}
326
- slippage={state.context.slippage}
327
- onSlippageChange={handleSlippageChange}
328
- />
329
-
330
- <AddressInputModal
331
- isOpen={isAddressModalOpen}
332
- onClose={() => setIsAddressModalOpen(false)}
333
- chainId={state.context.buyAsset.chainId}
334
- chainName={
335
- buyChainInfo?.name ?? state.context.buyAsset.networkName ?? state.context.buyAsset.name
336
- }
337
- currentAddress={customReceiveAddress || effectiveReceiveAddress || ''}
338
- onAddressChange={setCustomReceiveAddress}
339
- walletAddress={
340
- buyChainType === 'evm'
341
- ? walletAddress
342
- : buyChainType === 'utxo'
343
- ? bitcoin.address
344
- : buyChainType === 'solana'
345
- ? solana.address
346
- : undefined
347
- }
348
- />
349
- </div>
350
- )
351
- }
352
-
353
- type SwapWidgetCoreProps = {
354
- walletClient: unknown
355
- defaultSellAsset: Asset
356
- defaultBuyAsset: Asset
357
- defaultSlippage: string
358
- defaultReceiveAddress?: string
359
- apiClient: ReturnType<typeof createApiClient>
360
- theme: SwapWidgetProps['theme']
361
- showPoweredBy: boolean
362
- enableWalletConnection: boolean
363
- isBuyAssetLocked: boolean
364
- onConnectWallet?: () => void
365
- onSwapSuccess?: (txHash: string) => void
366
- onSwapError?: (error: Error) => void
367
- onAssetSelect?: (type: 'sell' | 'buy', asset: Asset) => void
368
- sellDisabledAssetIds: string[]
369
- buyDisabledAssetIds: string[]
370
- sellDisabledChainIds: string[]
371
- buyDisabledChainIds: string[]
372
- sellAllowedChainIds?: string[]
373
- buyAllowedChainIds?: string[]
374
- sellAllowedAssetIds?: string[]
375
- buyAllowedAssetIds?: string[]
376
- }
377
-
378
- const SwapWidgetCore = ({
379
- walletClient,
380
- defaultSellAsset,
381
- defaultBuyAsset,
382
- defaultSlippage,
383
- defaultReceiveAddress,
384
- apiClient,
385
- theme,
386
- showPoweredBy,
387
- enableWalletConnection,
388
- isBuyAssetLocked,
389
- onConnectWallet,
390
- onSwapSuccess,
391
- onSwapError,
392
- onAssetSelect,
393
- sellDisabledAssetIds,
394
- buyDisabledAssetIds,
395
- sellDisabledChainIds,
396
- buyDisabledChainIds,
397
- sellAllowedChainIds,
398
- buyAllowedChainIds,
399
- sellAllowedAssetIds,
400
- buyAllowedAssetIds,
401
- }: SwapWidgetCoreProps) => {
402
- const actorRef = SwapMachineCtx.useActorRef()
403
-
404
- const bitcoin = useBitcoinSigning()
405
- const solana = useSolanaSigning()
406
-
407
- const [customReceiveAddress, setCustomReceiveAddress] = useState<string>('')
408
-
409
- const walletAddress = useMemo(() => {
410
- if (!walletClient) return undefined
411
- return (walletClient as WalletClient).account?.address
412
- }, [walletClient])
413
-
414
- const buyChainId = SwapMachineCtx.useSelector(s => s.context.buyAsset.chainId)
415
- const buyChainType = getChainType(buyChainId)
416
-
417
- const effectiveReceiveAddress = useMemo(() => {
418
- if (customReceiveAddress) return customReceiveAddress
419
- if (defaultReceiveAddress) return defaultReceiveAddress
420
-
421
- if (buyChainType === 'utxo') return bitcoin.address ?? ''
422
- if (buyChainType === 'solana') return solana.address ?? ''
423
- if (buyChainType === 'evm') return walletAddress ?? ''
424
-
425
- return ''
426
- }, [
427
- customReceiveAddress,
428
- defaultReceiveAddress,
429
- buyChainType,
430
- bitcoin.address,
431
- solana.address,
432
- walletAddress,
433
- ])
434
-
435
- const isCustomAddress = useMemo(
436
- () => !!customReceiveAddress && customReceiveAddress !== walletAddress,
437
- [customReceiveAddress, walletAddress],
438
- )
439
-
440
- const initialSyncRef = useRef(false)
441
- useEffect(() => {
442
- if (initialSyncRef.current) return
443
- initialSyncRef.current = true
444
- actorRef.send({ type: 'SET_SELL_ASSET', asset: defaultSellAsset })
445
- actorRef.send({ type: 'SET_BUY_ASSET', asset: defaultBuyAsset })
446
- actorRef.send({ type: 'SET_SLIPPAGE', slippage: defaultSlippage })
447
- // eslint-disable-next-line react-hooks/exhaustive-deps -- defaults are initial-only, ref guard ensures single execution
448
- }, [actorRef])
449
-
450
- useEffect(() => {
451
- actorRef.send({ type: 'SET_WALLET_ADDRESS', address: walletAddress })
452
- }, [walletAddress, actorRef])
453
-
454
- useEffect(() => {
455
- actorRef.send({ type: 'SET_RECEIVE_ADDRESS', address: effectiveReceiveAddress })
456
- }, [effectiveReceiveAddress, actorRef])
457
-
458
- const walletValue: SwapWalletContextValue = useMemo(
459
- () => ({
460
- walletClient,
461
- walletAddress,
462
- effectiveReceiveAddress,
463
- isCustomAddress,
464
- customReceiveAddress,
465
- setCustomReceiveAddress,
466
- bitcoin,
467
- solana,
468
- }),
469
- [
470
- walletClient,
471
- walletAddress,
472
- effectiveReceiveAddress,
473
- isCustomAddress,
474
- customReceiveAddress,
475
- bitcoin,
476
- solana,
477
- ],
478
- )
479
-
480
- return (
481
- <SwapWalletProvider value={walletValue}>
482
- <SwapWidgetContent
483
- apiClient={apiClient}
484
- theme={theme}
485
- showPoweredBy={showPoweredBy}
486
- defaultReceiveAddress={defaultReceiveAddress}
487
- enableWalletConnection={enableWalletConnection}
488
- isBuyAssetLocked={isBuyAssetLocked}
489
- onConnectWallet={onConnectWallet}
490
- onSwapSuccess={onSwapSuccess}
491
- onSwapError={onSwapError}
492
- onAssetSelect={onAssetSelect}
493
- sellDisabledAssetIds={sellDisabledAssetIds}
494
- buyDisabledAssetIds={buyDisabledAssetIds}
495
- sellDisabledChainIds={sellDisabledChainIds}
496
- buyDisabledChainIds={buyDisabledChainIds}
497
- sellAllowedChainIds={sellAllowedChainIds}
498
- buyAllowedChainIds={buyAllowedChainIds}
499
- sellAllowedAssetIds={sellAllowedAssetIds}
500
- buyAllowedAssetIds={buyAllowedAssetIds}
501
- />
502
- </SwapWalletProvider>
503
- )
504
- }
505
-
506
- const SwapWidgetWithExternalWallet = (props: SwapWidgetProps) => {
507
- const apiClient = useMemo(
508
- () =>
509
- createApiClient({
510
- baseUrl: props.apiBaseUrl,
511
- affiliateAddress: props.affiliateAddress,
512
- }),
513
- [props.apiBaseUrl, props.affiliateAddress],
514
- )
515
-
516
- return (
517
- <QueryClientProvider client={queryClient}>
518
- <SwapMachineCtx.Provider>
519
- <SwapWidgetCore
520
- walletClient={props.walletClient}
521
- defaultSellAsset={props.defaultSellAsset ?? DEFAULT_SELL_ASSET}
522
- defaultBuyAsset={props.defaultBuyAsset ?? DEFAULT_BUY_ASSET}
523
- defaultSlippage={props.defaultSlippage ?? '0.5'}
524
- defaultReceiveAddress={props.defaultReceiveAddress}
525
- apiClient={apiClient}
526
- theme={props.theme}
527
- showPoweredBy={props.showPoweredBy ?? true}
528
- enableWalletConnection={false}
529
- isBuyAssetLocked={props.isBuyAssetLocked ?? false}
530
- onConnectWallet={props.onConnectWallet}
531
- onSwapSuccess={props.onSwapSuccess}
532
- onSwapError={props.onSwapError}
533
- onAssetSelect={props.onAssetSelect}
534
- sellDisabledAssetIds={props.sellDisabledAssetIds ?? props.disabledAssetIds ?? []}
535
- buyDisabledAssetIds={props.buyDisabledAssetIds ?? props.disabledAssetIds ?? []}
536
- sellDisabledChainIds={props.sellDisabledChainIds ?? props.disabledChainIds ?? []}
537
- buyDisabledChainIds={props.buyDisabledChainIds ?? props.disabledChainIds ?? []}
538
- sellAllowedChainIds={props.sellAllowedChainIds ?? props.allowedChainIds}
539
- buyAllowedChainIds={props.buyAllowedChainIds ?? props.allowedChainIds}
540
- sellAllowedAssetIds={props.sellAllowedAssetIds}
541
- buyAllowedAssetIds={props.buyAllowedAssetIds}
542
- />
543
- </SwapMachineCtx.Provider>
544
- </QueryClientProvider>
545
- )
546
- }
547
-
548
- const SwapWidgetWithInternalWallet = (
549
- props: SwapWidgetProps & { walletConnectProjectId: string },
550
- ) => {
551
- const apiClient = useMemo(
552
- () =>
553
- createApiClient({
554
- baseUrl: props.apiBaseUrl,
555
- affiliateAddress: props.affiliateAddress,
556
- }),
557
- [props.apiBaseUrl, props.affiliateAddress],
558
- )
559
-
560
- return (
561
- <InternalWalletProvider projectId={props.walletConnectProjectId}>
562
- {walletClient => (
563
- <QueryClientProvider client={queryClient}>
564
- <SwapMachineCtx.Provider>
565
- <SwapWidgetCore
566
- walletClient={walletClient}
567
- defaultSellAsset={props.defaultSellAsset ?? DEFAULT_SELL_ASSET}
568
- defaultBuyAsset={props.defaultBuyAsset ?? DEFAULT_BUY_ASSET}
569
- defaultSlippage={props.defaultSlippage ?? '0.5'}
570
- defaultReceiveAddress={props.defaultReceiveAddress}
571
- apiClient={apiClient}
572
- theme={props.theme}
573
- showPoweredBy={props.showPoweredBy ?? true}
574
- enableWalletConnection={true}
575
- onConnectWallet={props.onConnectWallet}
576
- onSwapSuccess={props.onSwapSuccess}
577
- onSwapError={props.onSwapError}
578
- onAssetSelect={props.onAssetSelect}
579
- sellDisabledAssetIds={props.sellDisabledAssetIds ?? props.disabledAssetIds ?? []}
580
- buyDisabledAssetIds={props.buyDisabledAssetIds ?? props.disabledAssetIds ?? []}
581
- sellDisabledChainIds={props.sellDisabledChainIds ?? props.disabledChainIds ?? []}
582
- buyDisabledChainIds={props.buyDisabledChainIds ?? props.disabledChainIds ?? []}
583
- sellAllowedChainIds={props.sellAllowedChainIds ?? props.allowedChainIds}
584
- buyAllowedChainIds={props.buyAllowedChainIds ?? props.allowedChainIds}
585
- sellAllowedAssetIds={props.sellAllowedAssetIds}
586
- buyAllowedAssetIds={props.buyAllowedAssetIds}
587
- isBuyAssetLocked={props.isBuyAssetLocked ?? false}
588
- />
589
- </SwapMachineCtx.Provider>
590
- </QueryClientProvider>
591
- )}
592
- </InternalWalletProvider>
593
- )
594
- }
595
-
596
- export const SwapWidget = (props: SwapWidgetProps) => {
597
- if (props.enableWalletConnection && props.walletConnectProjectId) {
598
- return (
599
- <SwapWidgetWithInternalWallet
600
- {...props}
601
- walletConnectProjectId={props.walletConnectProjectId}
602
- />
603
- )
604
- }
605
-
606
- return <SwapWidgetWithExternalWallet {...props} />
607
- }