@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,180 @@
1
+ import './SettingsModal.css'
2
+
3
+ import { useCallback, useEffect, useState } from 'react'
4
+
5
+ const SLIPPAGE_PRESETS = ['0.1', '0.5', '1.0']
6
+
7
+ const useLockBodyScroll = (isLocked: boolean) => {
8
+ useEffect(() => {
9
+ if (!isLocked) return
10
+ const originalOverflow = document.body.style.overflow
11
+ document.body.style.overflow = 'hidden'
12
+ return () => {
13
+ document.body.style.overflow = originalOverflow
14
+ }
15
+ }, [isLocked])
16
+ }
17
+
18
+ type SettingsModalProps = {
19
+ isOpen: boolean
20
+ onClose: () => void
21
+ slippage: string
22
+ onSlippageChange: (slippage: string) => void
23
+ }
24
+
25
+ export const SettingsModal = ({
26
+ isOpen,
27
+ onClose,
28
+ slippage,
29
+ onSlippageChange,
30
+ }: SettingsModalProps) => {
31
+ useLockBodyScroll(isOpen)
32
+ const [customSlippage, setCustomSlippage] = useState('')
33
+ const [isCustom, setIsCustom] = useState(!SLIPPAGE_PRESETS.includes(slippage))
34
+
35
+ const handlePresetClick = useCallback(
36
+ (preset: string) => {
37
+ setIsCustom(false)
38
+ setCustomSlippage('')
39
+ onSlippageChange(preset)
40
+ },
41
+ [onSlippageChange],
42
+ )
43
+
44
+ const handleCustomChange = useCallback(
45
+ (value: string) => {
46
+ const sanitized = value.replace(/[^0-9.]/g, '')
47
+ const parts = sanitized.split('.')
48
+ const formatted = parts.length > 2 ? `${parts[0]}.${parts.slice(1).join('')}` : sanitized
49
+
50
+ setCustomSlippage(formatted)
51
+ setIsCustom(true)
52
+
53
+ const numValue = parseFloat(formatted)
54
+ if (!isNaN(numValue) && numValue > 0 && numValue <= 50) {
55
+ onSlippageChange(formatted)
56
+ }
57
+ },
58
+ [onSlippageChange],
59
+ )
60
+
61
+ const handleBackdropClick = useCallback(
62
+ (e: React.MouseEvent) => {
63
+ if (e.target === e.currentTarget) {
64
+ onClose()
65
+ }
66
+ },
67
+ [onClose],
68
+ )
69
+
70
+ if (!isOpen) return null
71
+
72
+ const currentSlippageNum = parseFloat(slippage)
73
+ const isHighSlippage = currentSlippageNum > 1
74
+ const isVeryHighSlippage = currentSlippageNum > 5
75
+
76
+ return (
77
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
78
+ <div
79
+ className='ssw-modal-backdrop'
80
+ onClick={handleBackdropClick}
81
+ onKeyDown={e => e.key === 'Escape' && onClose()}
82
+ role='dialog'
83
+ aria-modal='true'
84
+ aria-labelledby='settings-modal-title'
85
+ >
86
+ <div className='ssw-settings-modal'>
87
+ <div className='ssw-modal-header'>
88
+ <h2 id='settings-modal-title' className='ssw-modal-title'>
89
+ Settings
90
+ </h2>
91
+ <button className='ssw-modal-close' onClick={onClose} type='button'>
92
+ <svg
93
+ width='20'
94
+ height='20'
95
+ viewBox='0 0 24 24'
96
+ fill='none'
97
+ stroke='currentColor'
98
+ strokeWidth='2'
99
+ >
100
+ <path d='M18 6L6 18M6 6l12 12' />
101
+ </svg>
102
+ </button>
103
+ </div>
104
+
105
+ <div className='ssw-settings-content'>
106
+ <div className='ssw-settings-section'>
107
+ <div className='ssw-settings-label'>
108
+ <span>Slippage Tolerance</span>
109
+ <button
110
+ className='ssw-info-btn'
111
+ type='button'
112
+ title="Maximum price difference you're willing to accept"
113
+ >
114
+ <svg
115
+ width='14'
116
+ height='14'
117
+ viewBox='0 0 24 24'
118
+ fill='none'
119
+ stroke='currentColor'
120
+ strokeWidth='2'
121
+ >
122
+ <circle cx='12' cy='12' r='10' />
123
+ <path d='M12 16v-4M12 8h.01' />
124
+ </svg>
125
+ </button>
126
+ </div>
127
+
128
+ <div className='ssw-slippage-options'>
129
+ {SLIPPAGE_PRESETS.map(preset => (
130
+ <button
131
+ key={preset}
132
+ className={`ssw-slippage-btn ${
133
+ !isCustom && slippage === preset ? 'ssw-selected' : ''
134
+ }`}
135
+ onClick={() => handlePresetClick(preset)}
136
+ type='button'
137
+ >
138
+ {preset}%
139
+ </button>
140
+ ))}
141
+ <div className={`ssw-slippage-custom ${isCustom ? 'ssw-selected' : ''}`}>
142
+ <input
143
+ type='text'
144
+ placeholder='Custom'
145
+ value={isCustom ? customSlippage || slippage : ''}
146
+ onChange={e => handleCustomChange(e.target.value)}
147
+ onFocus={() => {
148
+ setIsCustom(true)
149
+ if (!customSlippage) setCustomSlippage(slippage)
150
+ }}
151
+ />
152
+ <span className='ssw-slippage-suffix'>%</span>
153
+ </div>
154
+ </div>
155
+
156
+ {isHighSlippage && (
157
+ <div className={`ssw-slippage-warning ${isVeryHighSlippage ? 'ssw-error' : ''}`}>
158
+ <svg
159
+ width='16'
160
+ height='16'
161
+ viewBox='0 0 24 24'
162
+ fill='none'
163
+ stroke='currentColor'
164
+ strokeWidth='2'
165
+ >
166
+ <path d='M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0zM12 9v4M12 17h.01' />
167
+ </svg>
168
+ <span>
169
+ {isVeryHighSlippage
170
+ ? 'Very high slippage. Your transaction may be frontrun.'
171
+ : 'High slippage may result in unfavorable rates.'}
172
+ </span>
173
+ </div>
174
+ )}
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ )
180
+ }
@@ -0,0 +1,139 @@
1
+ import { useMemo } from 'react'
2
+
3
+ import type { SwapMachineContext, SwapMachineEvent } from '../machines/types'
4
+
5
+ type StatusStepProps = {
6
+ context: SwapMachineContext
7
+ send: (event: SwapMachineEvent) => void
8
+ isPolling: boolean
9
+ isComplete: boolean
10
+ isError: boolean
11
+ }
12
+
13
+ const ExplorerLink = ({ url }: { url: string }) => (
14
+ <a href={url} target='_blank' rel='noopener noreferrer' className='ssw-step-explorer-link'>
15
+ View on Explorer
16
+ <svg
17
+ width='12'
18
+ height='12'
19
+ viewBox='0 0 24 24'
20
+ fill='none'
21
+ stroke='currentColor'
22
+ strokeWidth='2'
23
+ >
24
+ <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' />
25
+ </svg>
26
+ </a>
27
+ )
28
+
29
+ export const StatusStep = ({ context, send, isPolling, isComplete, isError }: StatusStepProps) => {
30
+ const { sellAsset, buyAsset, txHash, error, retryCount, isSellAssetUtxo, isSellAssetSolana } =
31
+ context
32
+
33
+ const explorerUrl = useMemo(() => {
34
+ if (!txHash) return undefined
35
+ if (isSellAssetUtxo) return `https://mempool.space/tx/${txHash}`
36
+ if (isSellAssetSolana) return `https://solscan.io/tx/${txHash}`
37
+ return `${sellAsset.explorerTxLink ?? 'https://etherscan.io/tx/'}${txHash}`
38
+ }, [txHash, isSellAssetUtxo, isSellAssetSolana, sellAsset.explorerTxLink])
39
+
40
+ const truncatedError = useMemo(
41
+ () => (error && error.length > 100 ? `${error.slice(0, 100)}…` : error),
42
+ [error],
43
+ )
44
+
45
+ return (
46
+ <div className='ssw-step-screen'>
47
+ {isPolling && (
48
+ <>
49
+ <div className='ssw-step-icon-circle ssw-ic-accent'>
50
+ <svg
51
+ className='ssw-spinner'
52
+ width='32'
53
+ height='32'
54
+ viewBox='0 0 24 24'
55
+ fill='none'
56
+ stroke='currentColor'
57
+ strokeWidth='2'
58
+ >
59
+ <circle cx='12' cy='12' r='10' opacity='0.25' />
60
+ <path d='M12 2a10 10 0 0 1 10 10' />
61
+ </svg>
62
+ </div>
63
+ <div className='ssw-step-title'>Confirming Transaction</div>
64
+ <div className='ssw-step-subtitle'>Your swap is being processed…</div>
65
+ {explorerUrl && <ExplorerLink url={explorerUrl} />}
66
+ </>
67
+ )}
68
+
69
+ {isComplete && (
70
+ <>
71
+ <div className='ssw-step-icon-circle ssw-ic-success'>
72
+ <svg
73
+ width='32'
74
+ height='32'
75
+ viewBox='0 0 24 24'
76
+ fill='none'
77
+ stroke='currentColor'
78
+ strokeWidth='2.5'
79
+ >
80
+ <path d='M20 6L9 17l-5-5' />
81
+ </svg>
82
+ </div>
83
+ <div className='ssw-step-title'>Swap Complete!</div>
84
+ <div className='ssw-step-subtitle'>
85
+ Swapped {sellAsset.symbol} for {buyAsset.symbol}
86
+ </div>
87
+ {explorerUrl && <ExplorerLink url={explorerUrl} />}
88
+ <div className='ssw-step-actions'>
89
+ <button
90
+ className='ssw-action-btn'
91
+ onClick={() => send({ type: 'RESET' })}
92
+ type='button'
93
+ >
94
+ New Swap
95
+ </button>
96
+ </div>
97
+ </>
98
+ )}
99
+
100
+ {isError && (
101
+ <>
102
+ <div className='ssw-step-icon-circle ssw-ic-error'>
103
+ <svg
104
+ width='32'
105
+ height='32'
106
+ viewBox='0 0 24 24'
107
+ fill='none'
108
+ stroke='currentColor'
109
+ strokeWidth='2'
110
+ >
111
+ <circle cx='12' cy='12' r='10' />
112
+ <path d='M15 9l-6 6M9 9l6 6' />
113
+ </svg>
114
+ </div>
115
+ <div className='ssw-step-title'>Transaction Failed</div>
116
+ <div className='ssw-step-subtitle'>{truncatedError ?? 'Something went wrong'}</div>
117
+ <div className='ssw-step-actions'>
118
+ {retryCount < 3 && (
119
+ <button
120
+ className='ssw-action-btn'
121
+ onClick={() => send({ type: 'RETRY' })}
122
+ type='button'
123
+ >
124
+ Retry
125
+ </button>
126
+ )}
127
+ <button
128
+ className='ssw-action-btn ssw-secondary'
129
+ onClick={() => send({ type: 'RESET' })}
130
+ type='button'
131
+ >
132
+ Start Over
133
+ </button>
134
+ </div>
135
+ </>
136
+ )}
137
+ </div>
138
+ )
139
+ }