@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,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
- }