@shapeshiftoss/swap-widget 0.0.2

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 (157) hide show
  1. package/Dockerfile +87 -0
  2. package/README.md +519 -0
  3. package/dist/assets/PhArrowCircleDown-B305t-Sh.js +14 -0
  4. package/dist/assets/PhArrowClockwise-CJPxQhr_.js +14 -0
  5. package/dist/assets/PhArrowDown-C4hT9BVu.js +14 -0
  6. package/dist/assets/PhArrowLeft-Way4Cu-r.js +14 -0
  7. package/dist/assets/PhArrowRight-Cp5ZME0F.js +14 -0
  8. package/dist/assets/PhArrowSquareOut-NnrK69c5.js +14 -0
  9. package/dist/assets/PhArrowUp-Dph6j8rm.js +14 -0
  10. package/dist/assets/PhArrowUpRight-BpJjRRRh.js +14 -0
  11. package/dist/assets/PhArrowsClockwise-CFUT8X7R.js +14 -0
  12. package/dist/assets/PhArrowsDownUp-BjBI4ckC.js +14 -0
  13. package/dist/assets/PhArrowsLeftRight-CuLpbKRk.js +14 -0
  14. package/dist/assets/PhBank-Dchr4XQJ.js +14 -0
  15. package/dist/assets/PhBrowser-dUu2TrC1.js +14 -0
  16. package/dist/assets/PhCaretDown-78IuKU3V.js +14 -0
  17. package/dist/assets/PhCaretLeft-_aiyH7n1.js +14 -0
  18. package/dist/assets/PhCaretRight-8zJQEuDX.js +14 -0
  19. package/dist/assets/PhCaretUp-CiCxKWwN.js +14 -0
  20. package/dist/assets/PhCheck-CcADx8br.js +14 -0
  21. package/dist/assets/PhCircleHalf-BYzSzd2h.js +14 -0
  22. package/dist/assets/PhClock-UItiXeJq.js +14 -0
  23. package/dist/assets/PhCompass-CUy3uA7t.js +14 -0
  24. package/dist/assets/PhCopy-C-PDgSlZ.js +14 -0
  25. package/dist/assets/PhCreditCard-CWRaUT6F.js +14 -0
  26. package/dist/assets/PhCurrencyDollar-Cqla3AXT.js +14 -0
  27. package/dist/assets/PhDesktop-BBIPrYLl.js +14 -0
  28. package/dist/assets/PhDeviceMobile-BdsPaATg.js +14 -0
  29. package/dist/assets/PhDotsThree-BMXCQl-0.js +14 -0
  30. package/dist/assets/PhEnvelope-yUsis6yU.js +14 -0
  31. package/dist/assets/PhFunnelSimple-Ba7BbWTp.js +14 -0
  32. package/dist/assets/PhGlobe-DgLxBk5U.js +14 -0
  33. package/dist/assets/PhIdentificationCard-Czjmq9Z3.js +14 -0
  34. package/dist/assets/PhImage-BAJ_erz6.js +14 -0
  35. package/dist/assets/PhInfo-Cr4DhVUE.js +14 -0
  36. package/dist/assets/PhLightbulb-BX_7duTr.js +14 -0
  37. package/dist/assets/PhMagnifyingGlass-Mygfryvz.js +14 -0
  38. package/dist/assets/PhPaperPlaneRight-BeuCA660.js +14 -0
  39. package/dist/assets/PhPlus-BMjcHjsT.js +14 -0
  40. package/dist/assets/PhPower-CGffGmdb.js +14 -0
  41. package/dist/assets/PhPuzzlePiece-wm_GpVSi.js +14 -0
  42. package/dist/assets/PhQrCode-DRwRzXCH.js +14 -0
  43. package/dist/assets/PhQuestion-M09j2U0T.js +14 -0
  44. package/dist/assets/PhQuestionMark-BP9EMB6h.js +14 -0
  45. package/dist/assets/PhSealCheck-BfG80yKn.js +14 -0
  46. package/dist/assets/PhSignOut-C5YOWlPT.js +14 -0
  47. package/dist/assets/PhSpinner-BXooDYzU.js +14 -0
  48. package/dist/assets/PhTrash-Biq6rmmL.js +14 -0
  49. package/dist/assets/PhUser-CAgZSpGe.js +14 -0
  50. package/dist/assets/PhVault-l2PHF38a.js +14 -0
  51. package/dist/assets/PhWallet-w25D16It.js +14 -0
  52. package/dist/assets/PhWarning-DPEydIPw.js +14 -0
  53. package/dist/assets/PhWarningCircle-BqjT745H.js +14 -0
  54. package/dist/assets/PhX-s-SmVExa.js +14 -0
  55. package/dist/assets/SwapController-If0bFrNc.js +1 -0
  56. package/dist/assets/ccip-BBB7uCdL.js +1 -0
  57. package/dist/assets/ccip-CkkdfKKI.js +1 -0
  58. package/dist/assets/data-capture-D4gAy-Yc.js +242 -0
  59. package/dist/assets/email-B1AJehis.js +90 -0
  60. package/dist/assets/embedded-wallet-Q6DQopDk.js +410 -0
  61. package/dist/assets/features-B6JAPAFb.js +5 -0
  62. package/dist/assets/index-5jd8RPN9.js +7428 -0
  63. package/dist/assets/index-B4kNtSTk.js +1080 -0
  64. package/dist/assets/index-B753eAob.js +1 -0
  65. package/dist/assets/index-BZoLTv77.js +3 -0
  66. package/dist/assets/index-Bcw9UURX.js +3 -0
  67. package/dist/assets/index-BkrV-ahK.js +2 -0
  68. package/dist/assets/index-CRQzQ_up.js +9 -0
  69. package/dist/assets/index-CSW0Gt5y.js +132 -0
  70. package/dist/assets/index-CZ4jTIsk.js +138 -0
  71. package/dist/assets/index-Cwa8SB3h.js +61 -0
  72. package/dist/assets/index-DEwQgT4r.js +78 -0
  73. package/dist/assets/index-DVfQiQ1d.js +1 -0
  74. package/dist/assets/index-cV3UdaMs.css +1 -0
  75. package/dist/assets/index-hAmu622O.js +43 -0
  76. package/dist/assets/onramp-XcPNCLRU.js +422 -0
  77. package/dist/assets/pay-with-exchange-BeHt4OF4.js +280 -0
  78. package/dist/assets/property-C9JG5tag.js +27 -0
  79. package/dist/assets/receive-7ff4he2S.js +131 -0
  80. package/dist/assets/send-CkMBIRXT.js +554 -0
  81. package/dist/assets/socials-DmmxuLrP.js +250 -0
  82. package/dist/assets/swaps-IHAYRQ0x.js +1050 -0
  83. package/dist/assets/transactions-9Cxdk7o9.js +16 -0
  84. package/dist/assets/v4-BOvFkHkt.js +1 -0
  85. package/dist/assets/w3m-modal-kJzFnBfB.js +891 -0
  86. package/dist/assets/wallet-standard-Dv8LMoUB.js +1 -0
  87. package/dist/favicon.png +0 -0
  88. package/dist/favicon.svg +11 -0
  89. package/dist/icon-192x192.png +0 -0
  90. package/dist/icon-512x512.png +0 -0
  91. package/dist/index.html +33 -0
  92. package/index.html +32 -0
  93. package/package.json +59 -0
  94. package/public/favicon.png +0 -0
  95. package/public/favicon.svg +11 -0
  96. package/public/icon-192x192.png +0 -0
  97. package/public/icon-512x512.png +0 -0
  98. package/railway.toml +10 -0
  99. package/src/api/client.ts +95 -0
  100. package/src/components/AddressInputModal.css +173 -0
  101. package/src/components/AddressInputModal.tsx +250 -0
  102. package/src/components/ApprovalStep.tsx +92 -0
  103. package/src/components/ExecutionStep.tsx +27 -0
  104. package/src/components/InputStep.tsx +431 -0
  105. package/src/components/QuoteSelector.css +146 -0
  106. package/src/components/QuoteSelector.tsx +137 -0
  107. package/src/components/QuotesModal.css +250 -0
  108. package/src/components/QuotesModal.tsx +182 -0
  109. package/src/components/SettingsModal.css +145 -0
  110. package/src/components/SettingsModal.tsx +180 -0
  111. package/src/components/StatusStep.tsx +139 -0
  112. package/src/components/SwapWidget.css +713 -0
  113. package/src/components/SwapWidget.tsx +607 -0
  114. package/src/components/TokenSelectModal.css +438 -0
  115. package/src/components/TokenSelectModal.tsx +487 -0
  116. package/src/components/WalletProvider.tsx +92 -0
  117. package/src/config/appkit.ts +104 -0
  118. package/src/config/wagmi.ts +27 -0
  119. package/src/constants/chains.ts +72 -0
  120. package/src/constants/defaults.ts +28 -0
  121. package/src/constants/swappers.ts +48 -0
  122. package/src/constants/viemChains.ts +69 -0
  123. package/src/contexts/SwapWalletContext.tsx +25 -0
  124. package/src/demo/App.css +458 -0
  125. package/src/demo/App.tsx +481 -0
  126. package/src/demo/main.tsx +25 -0
  127. package/src/hooks/useAssets.ts +180 -0
  128. package/src/hooks/useBalances.ts +822 -0
  129. package/src/hooks/useBitcoinSigning.ts +276 -0
  130. package/src/hooks/useMarketData.ts +169 -0
  131. package/src/hooks/useSolanaSigning.ts +275 -0
  132. package/src/hooks/useStatusPolling.ts +137 -0
  133. package/src/hooks/useSwapApproval.ts +106 -0
  134. package/src/hooks/useSwapDisplayValues.ts +157 -0
  135. package/src/hooks/useSwapExecution.ts +199 -0
  136. package/src/hooks/useSwapHandlers.ts +119 -0
  137. package/src/hooks/useSwapQuote.ts +71 -0
  138. package/src/hooks/useSwapQuoting.ts +102 -0
  139. package/src/hooks/useSwapRates.ts +61 -0
  140. package/src/index.ts +43 -0
  141. package/src/machines/SwapMachineContext.ts +5 -0
  142. package/src/machines/__tests__/guards.test.ts +228 -0
  143. package/src/machines/__tests__/setup.test.ts +7 -0
  144. package/src/machines/__tests__/swapMachine.test.ts +544 -0
  145. package/src/machines/__tests__/types.test.ts +124 -0
  146. package/src/machines/guards.ts +33 -0
  147. package/src/machines/swapMachine.ts +288 -0
  148. package/src/machines/types.ts +54 -0
  149. package/src/services/transactionStatus.ts +322 -0
  150. package/src/types/index.ts +361 -0
  151. package/src/utils/addressValidation.ts +201 -0
  152. package/src/utils/redirect.ts +45 -0
  153. package/src/vite-env.d.ts +10 -0
  154. package/tsconfig.json +23 -0
  155. package/tsconfig.node.json +10 -0
  156. package/vite.config.ts +101 -0
  157. package/vitest.config.ts +18 -0
@@ -0,0 +1,250 @@
1
+ import './AddressInputModal.css'
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
+
5
+ import type { ChainId } from '../types'
6
+ import { getAddressFormatHint, validateAddress } from '../utils/addressValidation'
7
+
8
+ const useLockBodyScroll = (isLocked: boolean) => {
9
+ useEffect(() => {
10
+ if (!isLocked) return
11
+ const originalOverflow = document.body.style.overflow
12
+ document.body.style.overflow = 'hidden'
13
+ return () => {
14
+ document.body.style.overflow = originalOverflow
15
+ }
16
+ }, [isLocked])
17
+ }
18
+
19
+ type AddressInputModalProps = {
20
+ isOpen: boolean
21
+ onClose: () => void
22
+ chainId: ChainId
23
+ chainName: string
24
+ currentAddress: string
25
+ onAddressChange: (address: string) => void
26
+ walletAddress?: string
27
+ }
28
+
29
+ export const AddressInputModal = ({
30
+ isOpen,
31
+ onClose,
32
+ chainId,
33
+ chainName,
34
+ currentAddress,
35
+ onAddressChange,
36
+ walletAddress,
37
+ }: AddressInputModalProps) => {
38
+ useLockBodyScroll(isOpen)
39
+ const [inputValue, setInputValue] = useState(currentAddress)
40
+ const [hasInteracted, setHasInteracted] = useState(false)
41
+ const backdropRef = useRef<HTMLDivElement>(null)
42
+
43
+ useEffect(() => {
44
+ if (isOpen) {
45
+ setInputValue(currentAddress)
46
+ setHasInteracted(false)
47
+ backdropRef.current?.focus()
48
+ }
49
+ }, [isOpen, currentAddress])
50
+
51
+ useEffect(() => {
52
+ if (!isOpen) return
53
+ const handleKeyDown = (e: KeyboardEvent) => {
54
+ if (e.key === 'Escape') onClose()
55
+ }
56
+ document.addEventListener('keydown', handleKeyDown)
57
+ return () => document.removeEventListener('keydown', handleKeyDown)
58
+ }, [isOpen, onClose])
59
+
60
+ const validation = useMemo(() => {
61
+ if (!inputValue || !hasInteracted) {
62
+ return { valid: true, error: undefined }
63
+ }
64
+ return validateAddress(inputValue, chainId)
65
+ }, [inputValue, chainId, hasInteracted])
66
+
67
+ const formatHint = useMemo(() => getAddressFormatHint(chainId), [chainId])
68
+
69
+ const handleInputChange = useCallback((value: string) => {
70
+ setInputValue(value)
71
+ setHasInteracted(true)
72
+ }, [])
73
+
74
+ const handleUseWalletAddress = useCallback(() => {
75
+ if (walletAddress) {
76
+ setInputValue(walletAddress)
77
+ setHasInteracted(true)
78
+ }
79
+ }, [walletAddress])
80
+
81
+ const handleConfirm = useCallback(() => {
82
+ const result = validateAddress(inputValue, chainId)
83
+ if (result.valid) {
84
+ onAddressChange(inputValue)
85
+ onClose()
86
+ }
87
+ }, [inputValue, chainId, onAddressChange, onClose])
88
+
89
+ const handleClear = useCallback(() => {
90
+ setInputValue('')
91
+ setHasInteracted(false)
92
+ onAddressChange('')
93
+ onClose()
94
+ }, [onAddressChange, onClose])
95
+
96
+ const handleBackdropClick = useCallback(
97
+ (e: React.MouseEvent) => {
98
+ if (e.target === e.currentTarget) {
99
+ onClose()
100
+ }
101
+ },
102
+ [onClose],
103
+ )
104
+
105
+ const handleBackdropKeyDown = useCallback(
106
+ (e: React.KeyboardEvent) => {
107
+ if (e.key === 'Escape') {
108
+ onClose()
109
+ }
110
+ },
111
+ [onClose],
112
+ )
113
+
114
+ const isConfirmDisabled = useMemo(() => {
115
+ if (!inputValue) return true
116
+ return !validateAddress(inputValue, chainId).valid
117
+ }, [inputValue, chainId])
118
+
119
+ if (!isOpen) return null
120
+
121
+ return (
122
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
123
+ <div
124
+ ref={backdropRef}
125
+ className='ssw-modal-backdrop'
126
+ onClick={handleBackdropClick}
127
+ onKeyDown={handleBackdropKeyDown}
128
+ role='dialog'
129
+ aria-modal='true'
130
+ aria-labelledby='address-modal-title'
131
+ tabIndex={-1}
132
+ >
133
+ <div className='ssw-address-modal'>
134
+ <div className='ssw-modal-header'>
135
+ <h2 id='address-modal-title' className='ssw-modal-title'>
136
+ Receive Address
137
+ </h2>
138
+ <button className='ssw-modal-close' onClick={onClose} type='button'>
139
+ <svg
140
+ width='20'
141
+ height='20'
142
+ viewBox='0 0 24 24'
143
+ fill='none'
144
+ stroke='currentColor'
145
+ strokeWidth='2'
146
+ >
147
+ <path d='M18 6L6 18M6 6l12 12' />
148
+ </svg>
149
+ </button>
150
+ </div>
151
+
152
+ <div className='ssw-address-content'>
153
+ <div className='ssw-address-label'>
154
+ <span>Enter {chainName} address</span>
155
+ </div>
156
+
157
+ <div
158
+ className={`ssw-address-input-wrapper ${
159
+ !validation.valid && hasInteracted ? 'ssw-invalid' : ''
160
+ }`}
161
+ >
162
+ <input
163
+ type='text'
164
+ className='ssw-address-input'
165
+ placeholder={formatHint}
166
+ value={inputValue}
167
+ onChange={e => handleInputChange(e.target.value)}
168
+ autoFocus
169
+ spellCheck={false}
170
+ autoComplete='off'
171
+ />
172
+ {inputValue && (
173
+ <button
174
+ className='ssw-address-clear-btn'
175
+ onClick={() => {
176
+ setInputValue('')
177
+ setHasInteracted(false)
178
+ }}
179
+ type='button'
180
+ >
181
+ <svg
182
+ width='16'
183
+ height='16'
184
+ viewBox='0 0 24 24'
185
+ fill='none'
186
+ stroke='currentColor'
187
+ strokeWidth='2'
188
+ >
189
+ <path d='M18 6L6 18M6 6l12 12' />
190
+ </svg>
191
+ </button>
192
+ )}
193
+ </div>
194
+
195
+ {!validation.valid && hasInteracted && validation.error && (
196
+ <div className='ssw-address-error'>
197
+ <svg
198
+ width='14'
199
+ height='14'
200
+ viewBox='0 0 24 24'
201
+ fill='none'
202
+ stroke='currentColor'
203
+ strokeWidth='2'
204
+ >
205
+ <circle cx='12' cy='12' r='10' />
206
+ <path d='M12 8v4M12 16h.01' />
207
+ </svg>
208
+ <span>{validation.error}</span>
209
+ </div>
210
+ )}
211
+
212
+ {walletAddress && (
213
+ <button className='ssw-use-wallet-btn' onClick={handleUseWalletAddress} type='button'>
214
+ <svg
215
+ width='16'
216
+ height='16'
217
+ viewBox='0 0 24 24'
218
+ fill='none'
219
+ stroke='currentColor'
220
+ strokeWidth='2'
221
+ >
222
+ <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' />
223
+ <path d='M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4' />
224
+ </svg>
225
+ <span>Use connected wallet</span>
226
+ </button>
227
+ )}
228
+
229
+ <div className='ssw-address-actions'>
230
+ <button
231
+ className='ssw-address-btn ssw-address-btn-secondary'
232
+ onClick={handleClear}
233
+ type='button'
234
+ >
235
+ Reset to Wallet
236
+ </button>
237
+ <button
238
+ className='ssw-address-btn ssw-address-btn-primary'
239
+ onClick={handleConfirm}
240
+ disabled={isConfirmDisabled}
241
+ type='button'
242
+ >
243
+ Confirm
244
+ </button>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+ )
250
+ }
@@ -0,0 +1,92 @@
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
+ }
@@ -0,0 +1,27 @@
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
+ )