@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,92 +0,0 @@
1
- import type { SwapMachineContext, SwapMachineEvent } from '../machines/types'
2
-
3
- type ApprovalStepProps = {
4
- context: SwapMachineContext
5
- send: (event: SwapMachineEvent) => void
6
- isApproving: boolean
7
- }
8
-
9
- export const ApprovalStep = ({ context, send, isApproving }: ApprovalStepProps) => {
10
- const { sellAsset, approvalTxHash } = context
11
-
12
- const explorerUrl = approvalTxHash
13
- ? `${sellAsset.explorerTxLink ?? 'https://etherscan.io/tx/'}${approvalTxHash}`
14
- : undefined
15
-
16
- if (isApproving) {
17
- return (
18
- <div className='ssw-step-screen'>
19
- <div className='ssw-step-icon-circle ssw-ic-accent'>
20
- <svg
21
- className='ssw-spinner'
22
- width='32'
23
- height='32'
24
- viewBox='0 0 24 24'
25
- fill='none'
26
- stroke='currentColor'
27
- strokeWidth='2'
28
- >
29
- <circle cx='12' cy='12' r='10' opacity='0.25' />
30
- <path d='M12 2a10 10 0 0 1 10 10' />
31
- </svg>
32
- </div>
33
- <div className='ssw-step-title'>Approving {sellAsset.symbol}…</div>
34
- <div className='ssw-step-subtitle'>Waiting for wallet confirmation</div>
35
- {explorerUrl && (
36
- <a
37
- href={explorerUrl}
38
- target='_blank'
39
- rel='noopener noreferrer'
40
- className='ssw-step-explorer-link'
41
- >
42
- View on Explorer
43
- <svg
44
- width='12'
45
- height='12'
46
- viewBox='0 0 24 24'
47
- fill='none'
48
- stroke='currentColor'
49
- strokeWidth='2'
50
- >
51
- <path d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3' />
52
- </svg>
53
- </a>
54
- )}
55
- </div>
56
- )
57
- }
58
-
59
- return (
60
- <div className='ssw-step-screen'>
61
- <div className='ssw-step-icon-circle ssw-ic-accent'>
62
- <svg
63
- width='32'
64
- height='32'
65
- viewBox='0 0 24 24'
66
- fill='none'
67
- stroke='currentColor'
68
- strokeWidth='2'
69
- >
70
- <rect x='3' y='11' width='18' height='11' rx='2' ry='2' />
71
- <path d='M7 11V7a5 5 0 0 1 10 0v4' />
72
- </svg>
73
- </div>
74
- <div className='ssw-step-title'>Token Approval Required</div>
75
- <div className='ssw-step-subtitle'>
76
- Allow the swap contract to use your {sellAsset.symbol}
77
- </div>
78
- <div className='ssw-step-actions'>
79
- <button className='ssw-action-btn' onClick={() => send({ type: 'APPROVE' })} type='button'>
80
- Approve {sellAsset.symbol}
81
- </button>
82
- <button
83
- className='ssw-action-btn ssw-secondary'
84
- onClick={() => send({ type: 'RESET' })}
85
- type='button'
86
- >
87
- Back
88
- </button>
89
- </div>
90
- </div>
91
- )
92
- }
@@ -1,27 +0,0 @@
1
- import type { SwapMachineContext, SwapMachineEvent } from '../machines/types'
2
-
3
- type ExecutionStepProps = {
4
- context: SwapMachineContext
5
- send: (event: SwapMachineEvent) => void
6
- }
7
-
8
- export const ExecutionStep = ({ context: _context, send: _send }: ExecutionStepProps) => (
9
- <div className='ssw-step-screen'>
10
- <div className='ssw-step-icon-circle ssw-ic-accent'>
11
- <svg
12
- className='ssw-spinner'
13
- width='32'
14
- height='32'
15
- viewBox='0 0 24 24'
16
- fill='none'
17
- stroke='currentColor'
18
- strokeWidth='2'
19
- >
20
- <circle cx='12' cy='12' r='10' opacity='0.25' />
21
- <path d='M12 2a10 10 0 0 1 10 10' />
22
- </svg>
23
- </div>
24
- <div className='ssw-step-title'>Confirm in Wallet</div>
25
- <div className='ssw-step-subtitle'>Please sign the transaction in your wallet</div>
26
- </div>
27
- )
@@ -1,431 +0,0 @@
1
- import { useMemo } from 'react'
2
-
3
- import type { SwapMachineContext, SwapMachineEvent } from '../machines/types'
4
- import type { TradeRate } from '../types'
5
- import { formatAmount, truncateAddress } from '../types'
6
- import { QuoteSelector } from './QuoteSelector'
7
-
8
- type InputStepProps = {
9
- context: SwapMachineContext
10
- send: (event: SwapMachineEvent) => void
11
- rates: TradeRate[]
12
- isLoadingRates: boolean
13
- ratesError: Error | null
14
- sellAssetBalance: { balance: string; balanceFormatted: string } | undefined
15
- buyAssetBalance: { balance: string; balanceFormatted: string } | undefined
16
- isSellBalanceLoading: boolean
17
- isBuyBalanceLoading: boolean
18
- sellUsdValue: string
19
- buyUsdValue: string
20
- sellChainInfo: { name: string } | undefined
21
- buyChainInfo: { name: string } | undefined
22
- displayRate: TradeRate | undefined
23
- walletAddress: string | undefined
24
- bitcoinAddress: string | undefined
25
- solanaAddress: string | undefined
26
- defaultReceiveAddress: string | undefined
27
- buyAssetUsdPrice: string | undefined
28
- onOpenTokenModal: (type: 'sell' | 'buy') => void
29
- onOpenAddressModal: () => void
30
- enableWalletConnection: boolean
31
- onConnectWallet?: () => void
32
- bitcoinState: { isLoading: boolean }
33
- solanaState: { isLoading: boolean }
34
- isQuoting: boolean
35
- isExecuting: boolean
36
- effectiveReceiveAddress: string
37
- isCustomAddress: boolean
38
- sellAmount: string
39
- onSellAmountChange: (value: string) => void
40
- onSwapTokens: () => void
41
- onSelectRate: (rate: TradeRate) => void
42
- onButtonClick: () => void
43
- sellAmountBaseUnit: string | undefined
44
- networkFeeDisplay: string | undefined
45
- sellBalanceFiatValue: string | undefined
46
- buyBalanceFiatValue: string | undefined
47
- isBuyAssetLocked: boolean
48
- }
49
-
50
- export const InputStep = ({
51
- context,
52
- send: _send,
53
- rates,
54
- isLoadingRates,
55
- ratesError,
56
- sellAssetBalance,
57
- buyAssetBalance,
58
- isSellBalanceLoading,
59
- isBuyBalanceLoading,
60
- sellUsdValue,
61
- buyUsdValue,
62
- sellChainInfo,
63
- buyChainInfo,
64
- displayRate,
65
- walletAddress,
66
- bitcoinAddress,
67
- solanaAddress,
68
- defaultReceiveAddress,
69
- buyAssetUsdPrice,
70
- onOpenTokenModal,
71
- onOpenAddressModal,
72
- enableWalletConnection: _enableWalletConnection,
73
- onConnectWallet: _onConnectWallet,
74
- bitcoinState,
75
- solanaState,
76
- isQuoting,
77
- isExecuting,
78
- effectiveReceiveAddress,
79
- isCustomAddress,
80
- sellAmount,
81
- onSellAmountChange,
82
- onSwapTokens,
83
- onSelectRate,
84
- onButtonClick,
85
- sellAmountBaseUnit,
86
- networkFeeDisplay,
87
- sellBalanceFiatValue,
88
- buyBalanceFiatValue,
89
- isBuyAssetLocked,
90
- }: InputStepProps) => {
91
- const { sellAsset, buyAsset, selectedRate, isSellAssetEvm, isSellAssetUtxo, isSellAssetSolana } =
92
- context
93
-
94
- const canExecuteDirectly = isSellAssetEvm
95
- const canExecuteUtxo = isSellAssetUtxo
96
- const canExecuteSolana = isSellAssetSolana
97
- const isUnsupportedChain = !isSellAssetEvm && !isSellAssetUtxo && !isSellAssetSolana
98
-
99
- const isBitcoinConnected = !!bitcoinAddress
100
- const isSolanaConnected = !!solanaAddress
101
-
102
- const buyAmount = displayRate?.buyAmountCryptoBaseUnit
103
-
104
- const buttonText = useMemo(() => {
105
- if (isSellAssetUtxo && canExecuteUtxo) {
106
- if (!sellAmount) return 'Enter an amount'
107
- if (!isBitcoinConnected) return 'Connect Bitcoin Wallet'
108
- if (!effectiveReceiveAddress) return 'Enter receive address'
109
- if (bitcoinState.isLoading || isExecuting) return 'Executing...'
110
- if (isLoadingRates) return 'Finding rates...'
111
- if (ratesError) return 'No routes available'
112
- if (!rates?.length) return 'No routes found'
113
- return 'Swap'
114
- }
115
- if (isSellAssetSolana && canExecuteSolana) {
116
- if (!sellAmount) return 'Enter an amount'
117
- if (!isSolanaConnected) return 'Connect Solana Wallet'
118
- if (!effectiveReceiveAddress) return 'Enter receive address'
119
- if (solanaState.isLoading || isExecuting) return 'Executing...'
120
- if (isLoadingRates) return 'Finding rates...'
121
- if (ratesError) return 'No routes available'
122
- if (!rates?.length) return 'No routes found'
123
- return 'Swap'
124
- }
125
- if (!isSellAssetEvm) return 'Proceed on ShapeShift'
126
- if (!sellAmount) return 'Enter an amount'
127
- if (!walletAddress && canExecuteDirectly) return 'Connect Wallet'
128
- if (!effectiveReceiveAddress) return 'Enter receive address'
129
- if (isLoadingRates) return 'Finding rates...'
130
- if (ratesError) return 'No routes available'
131
- if (!rates?.length) return 'No routes found'
132
- if (isExecuting) return 'Executing...'
133
- return 'Swap'
134
- }, [
135
- walletAddress,
136
- canExecuteDirectly,
137
- canExecuteUtxo,
138
- canExecuteSolana,
139
- isSellAssetEvm,
140
- isSellAssetUtxo,
141
- isSellAssetSolana,
142
- isBitcoinConnected,
143
- isSolanaConnected,
144
- bitcoinState.isLoading,
145
- solanaState.isLoading,
146
- sellAmount,
147
- isLoadingRates,
148
- ratesError,
149
- rates,
150
- isExecuting,
151
- effectiveReceiveAddress,
152
- ])
153
-
154
- const isButtonDisabled = useMemo(() => {
155
- if (isUnsupportedChain) return false
156
-
157
- if (!sellAmount || isLoadingRates || ratesError || !rates?.length || isExecuting) {
158
- return true
159
- }
160
-
161
- if (!effectiveReceiveAddress) {
162
- return true
163
- }
164
-
165
- if (isSellAssetUtxo && canExecuteUtxo) {
166
- return !isBitcoinConnected || bitcoinState.isLoading
167
- }
168
-
169
- if (isSellAssetSolana && canExecuteSolana) {
170
- return !isSolanaConnected || solanaState.isLoading
171
- }
172
-
173
- return false
174
- }, [
175
- isUnsupportedChain,
176
- isSellAssetUtxo,
177
- isSellAssetSolana,
178
- canExecuteUtxo,
179
- canExecuteSolana,
180
- isBitcoinConnected,
181
- isSolanaConnected,
182
- bitcoinState.isLoading,
183
- solanaState.isLoading,
184
- sellAmount,
185
- isLoadingRates,
186
- ratesError,
187
- rates,
188
- isExecuting,
189
- effectiveReceiveAddress,
190
- ])
191
-
192
- return (
193
- <>
194
- <div className='ssw-swap-container'>
195
- <div className='ssw-token-section ssw-sell'>
196
- <div className='ssw-section-header'>
197
- <span className='ssw-section-label'>Sell</span>
198
- {walletAddress && isSellAssetEvm && (
199
- <span className='ssw-wallet-badge'>{truncateAddress(walletAddress)}</span>
200
- )}
201
- {bitcoinAddress && isSellAssetUtxo && (
202
- <span className='ssw-wallet-badge'>{truncateAddress(bitcoinAddress)}</span>
203
- )}
204
- {solanaAddress && isSellAssetSolana && (
205
- <span className='ssw-wallet-badge'>{truncateAddress(solanaAddress)}</span>
206
- )}
207
- </div>
208
-
209
- <div className='ssw-input-row'>
210
- <input
211
- type='text'
212
- className='ssw-amount-input'
213
- placeholder='0'
214
- value={sellAmount}
215
- onChange={e => {
216
- const raw = e.target.value.replace(/[^0-9.]/g, '')
217
- const parts = raw.split('.')
218
- const sanitized = parts.length > 2 ? parts[0] + '.' + parts.slice(1).join('') : raw
219
- onSellAmountChange(sanitized)
220
- }}
221
- />
222
- <button
223
- className='ssw-token-btn'
224
- onClick={() => onOpenTokenModal('sell')}
225
- type='button'
226
- >
227
- {sellAsset.icon ? (
228
- <img src={sellAsset.icon} alt={sellAsset.symbol} className='ssw-token-icon' />
229
- ) : (
230
- <div className='ssw-token-icon-placeholder'>{sellAsset.symbol.charAt(0)}</div>
231
- )}
232
- <div className='ssw-token-info'>
233
- <span className='ssw-token-symbol'>{sellAsset.symbol}</span>
234
- <span className='ssw-token-chain'>
235
- {sellChainInfo?.name ?? sellAsset.networkName ?? sellAsset.name}
236
- </span>
237
- </div>
238
- <svg
239
- width='16'
240
- height='16'
241
- viewBox='0 0 24 24'
242
- fill='none'
243
- stroke='currentColor'
244
- strokeWidth='2'
245
- >
246
- <path d='M9 18l6-6-6-6' />
247
- </svg>
248
- </button>
249
- </div>
250
-
251
- <div className='ssw-section-footer'>
252
- <span className='ssw-usd-value'>{sellUsdValue}</span>
253
- {(walletAddress || bitcoinAddress || solanaAddress) &&
254
- (isSellBalanceLoading ? (
255
- <span className='ssw-balance-skeleton' />
256
- ) : sellAssetBalance ? (
257
- <span className='ssw-balance'>
258
- Balance: {sellAssetBalance.balanceFormatted}
259
- {sellBalanceFiatValue && (
260
- <span className='ssw-balance-fiat'> ({sellBalanceFiatValue})</span>
261
- )}
262
- </span>
263
- ) : null)}
264
- </div>
265
- </div>
266
-
267
- <div className='ssw-swap-divider'>
268
- <button
269
- className='ssw-swap-btn'
270
- onClick={isBuyAssetLocked ? undefined : onSwapTokens}
271
- disabled={isBuyAssetLocked}
272
- type='button'
273
- >
274
- <svg
275
- width='16'
276
- height='16'
277
- viewBox='0 0 24 24'
278
- fill='none'
279
- stroke='currentColor'
280
- strokeWidth='2'
281
- >
282
- <path d='M12 5v14M5 12l7 7 7-7' />
283
- </svg>
284
- </button>
285
- </div>
286
-
287
- <div className='ssw-token-section ssw-buy'>
288
- <div className='ssw-section-header'>
289
- <span className='ssw-section-label'>Buy</span>
290
- {defaultReceiveAddress ? (
291
- <span className='ssw-receive-address-btn ssw-receive-address-readonly'>
292
- <span className='ssw-receive-address-text'>
293
- {truncateAddress(defaultReceiveAddress, 4)}
294
- </span>
295
- </span>
296
- ) : (
297
- <button
298
- className={`ssw-receive-address-btn ${isCustomAddress ? 'ssw-custom' : ''}`}
299
- onClick={() => onOpenAddressModal()}
300
- type='button'
301
- >
302
- <span className='ssw-receive-address-text'>
303
- {effectiveReceiveAddress
304
- ? truncateAddress(effectiveReceiveAddress, 4)
305
- : 'Enter address'}
306
- </span>
307
- <svg
308
- width='12'
309
- height='12'
310
- viewBox='0 0 24 24'
311
- fill='none'
312
- stroke='currentColor'
313
- strokeWidth='2'
314
- >
315
- <path d='M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7' />
316
- <path d='M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z' />
317
- </svg>
318
- </button>
319
- )}
320
- </div>
321
-
322
- <div className='ssw-input-row'>
323
- <input
324
- type='text'
325
- className='ssw-amount-input'
326
- placeholder='0'
327
- value={buyAmount ? formatAmount(buyAmount, buyAsset.precision) : ''}
328
- readOnly
329
- />
330
- <button
331
- className={`ssw-token-btn${isBuyAssetLocked ? ' ssw-token-btn-locked' : ''}`}
332
- onClick={isBuyAssetLocked ? undefined : () => onOpenTokenModal('buy')}
333
- disabled={isBuyAssetLocked}
334
- type='button'
335
- >
336
- {buyAsset.icon ? (
337
- <img src={buyAsset.icon} alt={buyAsset.symbol} className='ssw-token-icon' />
338
- ) : (
339
- <div className='ssw-token-icon-placeholder'>{buyAsset.symbol.charAt(0)}</div>
340
- )}
341
- <div className='ssw-token-info'>
342
- <span className='ssw-token-symbol'>{buyAsset.symbol}</span>
343
- <span className='ssw-token-chain'>
344
- {buyChainInfo?.name ?? buyAsset.networkName ?? buyAsset.name}
345
- </span>
346
- </div>
347
- {!isBuyAssetLocked && (
348
- <svg
349
- width='16'
350
- height='16'
351
- viewBox='0 0 24 24'
352
- fill='none'
353
- stroke='currentColor'
354
- strokeWidth='2'
355
- >
356
- <path d='M9 18l6-6-6-6' />
357
- </svg>
358
- )}
359
- </button>
360
- </div>
361
-
362
- <div className='ssw-section-footer'>
363
- <span className='ssw-usd-value'>{buyUsdValue}</span>
364
- {(walletAddress || bitcoinAddress || solanaAddress) &&
365
- (isBuyBalanceLoading ? (
366
- <span className='ssw-balance-skeleton' />
367
- ) : buyAssetBalance ? (
368
- <span className='ssw-balance'>
369
- Balance: {buyAssetBalance.balanceFormatted}
370
- {buyBalanceFiatValue && (
371
- <span className='ssw-balance-fiat'> ({buyBalanceFiatValue})</span>
372
- )}
373
- </span>
374
- ) : null)}
375
- </div>
376
- </div>
377
- </div>
378
-
379
- {sellAmountBaseUnit && sellAmountBaseUnit !== '0' && (rates?.length || isLoadingRates) && (
380
- <div className='ssw-quotes'>
381
- <QuoteSelector
382
- rates={rates ?? []}
383
- selectedRate={selectedRate}
384
- onSelectRate={onSelectRate}
385
- buyAsset={buyAsset}
386
- sellAsset={sellAsset}
387
- sellAmountBaseUnit={sellAmountBaseUnit}
388
- isLoading={isLoadingRates}
389
- buyAssetUsdPrice={buyAssetUsdPrice}
390
- />
391
- </div>
392
- )}
393
-
394
- {networkFeeDisplay && (
395
- <div className='ssw-network-fee'>
396
- <span className='ssw-network-fee-label'>Est. network fee</span>
397
- <span className='ssw-network-fee-value'>{networkFeeDisplay}</span>
398
- </div>
399
- )}
400
-
401
- <button
402
- className={`ssw-action-btn ${isUnsupportedChain ? 'ssw-secondary' : ''}`}
403
- disabled={isButtonDisabled || isQuoting}
404
- onClick={onButtonClick}
405
- type='button'
406
- style={isQuoting ? { opacity: 0.7 } : undefined}
407
- >
408
- {isQuoting ? (
409
- <>
410
- <svg
411
- className='ssw-spinner'
412
- width='16'
413
- height='16'
414
- viewBox='0 0 24 24'
415
- fill='none'
416
- stroke='currentColor'
417
- strokeWidth='2'
418
- style={{ display: 'inline-block', verticalAlign: 'middle', marginRight: '6px' }}
419
- >
420
- <circle cx='12' cy='12' r='10' opacity='0.25' />
421
- <path d='M12 2a10 10 0 0 1 10 10' />
422
- </svg>
423
- Fetching Quote…
424
- </>
425
- ) : (
426
- buttonText
427
- )}
428
- </button>
429
- </>
430
- )
431
- }
@@ -1,146 +0,0 @@
1
- .ssw-quote-selector {
2
- width: 100%;
3
- padding: 14px 16px;
4
- border-radius: var(--ssw-radius-md);
5
- background: var(--ssw-bg-tertiary);
6
- border: 1px solid var(--ssw-border);
7
- cursor: pointer;
8
- transition: all 0.15s ease;
9
- display: flex;
10
- align-items: center;
11
- justify-content: space-between;
12
- gap: 12px;
13
- }
14
-
15
- .ssw-quote-selector:hover {
16
- border-color: var(--ssw-border-hover);
17
- background: var(--ssw-bg-hover);
18
- }
19
-
20
- .ssw-quote-selector.ssw-loading {
21
- cursor: default;
22
- justify-content: center;
23
- }
24
-
25
- .ssw-quote-selector.ssw-loading:hover {
26
- border-color: var(--ssw-border);
27
- background: var(--ssw-bg-tertiary);
28
- }
29
-
30
- .ssw-quote-loading {
31
- display: flex;
32
- align-items: center;
33
- justify-content: center;
34
- gap: 10px;
35
- color: var(--ssw-text-secondary);
36
- font-size: 14px;
37
- }
38
-
39
- .ssw-spinner-small {
40
- width: 16px;
41
- height: 16px;
42
- border: 2px solid var(--ssw-border);
43
- border-top-color: var(--ssw-accent);
44
- border-radius: 50%;
45
- animation: ssw-spin 0.8s linear infinite;
46
- }
47
-
48
- @keyframes ssw-spin {
49
- to {
50
- transform: rotate(360deg);
51
- }
52
- }
53
-
54
- .ssw-quote-left {
55
- display: flex;
56
- flex-direction: column;
57
- gap: 4px;
58
- align-items: flex-start;
59
- }
60
-
61
- .ssw-quote-provider {
62
- display: flex;
63
- align-items: center;
64
- gap: 10px;
65
- }
66
-
67
- .ssw-quote-provider-icon {
68
- width: 20px;
69
- height: 20px;
70
- border-radius: var(--ssw-radius-xs);
71
- object-fit: contain;
72
- background: var(--ssw-bg-secondary);
73
- }
74
-
75
- .ssw-quote-provider-icon-placeholder {
76
- width: 20px;
77
- height: 20px;
78
- border-radius: var(--ssw-radius-xs);
79
- display: flex;
80
- align-items: center;
81
- justify-content: center;
82
- font-size: 12px;
83
- font-weight: 600;
84
- color: white;
85
- }
86
-
87
- .ssw-quote-provider-name {
88
- font-size: 14px;
89
- font-weight: 600;
90
- color: var(--ssw-text-primary);
91
- }
92
-
93
- .ssw-quote-best-tag {
94
- padding: 3px 7px;
95
- border-radius: var(--ssw-radius-xs);
96
- background: var(--ssw-accent);
97
- color: white;
98
- font-size: 10px;
99
- font-weight: 600;
100
- text-transform: uppercase;
101
- letter-spacing: 0.03em;
102
- }
103
-
104
- .ssw-quote-usd {
105
- font-size: 13px;
106
- color: var(--ssw-text-secondary);
107
- padding-left: 38px;
108
- }
109
-
110
- .ssw-quote-right {
111
- display: flex;
112
- flex-direction: column;
113
- align-items: flex-end;
114
- gap: 4px;
115
- }
116
-
117
- .ssw-quote-amount-row {
118
- display: flex;
119
- align-items: baseline;
120
- gap: 5px;
121
- }
122
-
123
- .ssw-quote-amount {
124
- font-size: 17px;
125
- font-weight: 600;
126
- color: var(--ssw-text-primary);
127
- }
128
-
129
- .ssw-quote-symbol {
130
- font-size: 13px;
131
- color: var(--ssw-text-secondary);
132
- font-weight: 500;
133
- }
134
-
135
- .ssw-quote-more {
136
- display: flex;
137
- align-items: center;
138
- gap: 3px;
139
- font-size: 12px;
140
- color: var(--ssw-accent);
141
- font-weight: 500;
142
- }
143
-
144
- .ssw-quote-more svg {
145
- opacity: 0.9;
146
- }