@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,33 +0,0 @@
1
- import type { SwapMachineContext } from './types'
2
-
3
- export const hasValidInput = (context: SwapMachineContext): boolean => {
4
- return (
5
- !!context.sellAsset &&
6
- !!context.buyAsset &&
7
- !!context.sellAmountBaseUnit &&
8
- context.sellAmountBaseUnit !== '0'
9
- )
10
- }
11
-
12
- export const hasQuote = (context: SwapMachineContext): boolean => context.quote !== null
13
-
14
- export const isApprovalRequired = (context: SwapMachineContext): boolean => {
15
- if (context.quote?.approval?.isRequired !== true || context.chainType !== 'evm') return false
16
- const assetIdParts = context.sellAsset.assetId.split('/')
17
- const namespace = assetIdParts[1]?.split(':')[0]
18
- return namespace === 'erc20'
19
- }
20
-
21
- export const canRetry = (context: SwapMachineContext): boolean => context.retryCount < 3
22
-
23
- export const isEvmChain = (context: SwapMachineContext): boolean => context.chainType === 'evm'
24
-
25
- export const isUtxoChain = (context: SwapMachineContext): boolean => context.chainType === 'utxo'
26
-
27
- export const isSolanaChain = (context: SwapMachineContext): boolean =>
28
- context.chainType === 'solana'
29
-
30
- export const hasWallet = (context: SwapMachineContext): boolean => !!context.walletAddress
31
-
32
- export const hasReceiveAddress = (context: SwapMachineContext): boolean =>
33
- !!context.effectiveReceiveAddress
@@ -1,288 +0,0 @@
1
- import { assign, setup } from 'xstate'
2
-
3
- import { DEFAULT_BUY_ASSET, DEFAULT_SELL_ASSET } from '../constants/defaults'
4
- import type { Asset, QuoteResponse, TradeRate } from '../types'
5
- import { getChainType, parseAmount } from '../types'
6
- import * as guardFns from './guards'
7
- import type { SwapMachineContext, SwapMachineEvent } from './types'
8
-
9
- export const createInitialContext = (input?: {
10
- sellAsset?: Asset
11
- buyAsset?: Asset
12
- slippage?: string
13
- }): SwapMachineContext => {
14
- const sellAsset = input?.sellAsset ?? DEFAULT_SELL_ASSET
15
- const buyAsset = input?.buyAsset ?? DEFAULT_BUY_ASSET
16
- const sellChainType = getChainType(sellAsset.chainId)
17
- const buyChainType = getChainType(buyAsset.chainId)
18
-
19
- return {
20
- sellAsset,
21
- buyAsset,
22
- sellAmount: '',
23
- sellAmountBaseUnit: undefined,
24
- selectedRate: null,
25
- quote: null,
26
- txHash: null,
27
- approvalTxHash: null,
28
- error: null,
29
- errorSource: null,
30
- retryCount: 0,
31
- chainType: sellChainType,
32
- slippage: input?.slippage ?? '0.5',
33
- walletAddress: undefined,
34
- effectiveReceiveAddress: '',
35
- isSellAssetEvm: sellChainType === 'evm',
36
- isSellAssetUtxo: sellChainType === 'utxo',
37
- isSellAssetSolana: sellChainType === 'solana',
38
- isBuyAssetEvm: buyChainType === 'evm',
39
- }
40
- }
41
-
42
- export const swapMachine = setup({
43
- types: {
44
- context: {} as SwapMachineContext,
45
- events: {} as SwapMachineEvent,
46
- },
47
- guards: {
48
- hasValidInput: ({ context }) => guardFns.hasValidInput(context),
49
- isApprovalRequired: ({ context, event }) => {
50
- const quote = (event as { type: 'QUOTE_SUCCESS'; quote: QuoteResponse }).quote
51
- if (quote?.approval?.isRequired !== true || context.chainType !== 'evm') return false
52
- const namespace = context.sellAsset.assetId.split('/')[1]?.split(':')[0]
53
- return namespace === 'erc20'
54
- },
55
- canRetry: ({ context }) => guardFns.canRetry(context),
56
- isQuoteError: ({ context }) => context.errorSource === 'QUOTE_ERROR',
57
- isApprovalError: ({ context }) => context.errorSource === 'APPROVAL_ERROR',
58
- isStatusFailed: ({ context }) => context.errorSource === 'STATUS_FAILED',
59
- hasQuote: ({ context }) => guardFns.hasQuote(context),
60
- isEvmChain: ({ context }) => guardFns.isEvmChain(context),
61
- isUtxoChain: ({ context }) => guardFns.isUtxoChain(context),
62
- isSolanaChain: ({ context }) => guardFns.isSolanaChain(context),
63
- hasWallet: ({ context }) => guardFns.hasWallet(context),
64
- hasReceiveAddress: ({ context }) => guardFns.hasReceiveAddress(context),
65
- },
66
- actions: {
67
- assignSellAsset: assign(({ context, event }) => {
68
- const { asset } = event as { type: 'SET_SELL_ASSET'; asset: Asset }
69
- const chainType = getChainType(asset.chainId)
70
- return {
71
- sellAsset: asset,
72
- sellAmountBaseUnit: context.sellAmount
73
- ? parseAmount(context.sellAmount, asset.precision)
74
- : undefined,
75
- chainType,
76
- isSellAssetEvm: chainType === 'evm',
77
- isSellAssetUtxo: chainType === 'utxo',
78
- isSellAssetSolana: chainType === 'solana',
79
- selectedRate: null,
80
- quote: null,
81
- }
82
- }),
83
- assignBuyAsset: assign(({ event }) => {
84
- const { asset } = event as { type: 'SET_BUY_ASSET'; asset: Asset }
85
- const buyChainType = getChainType(asset.chainId)
86
- return {
87
- buyAsset: asset,
88
- isBuyAssetEvm: buyChainType === 'evm',
89
- selectedRate: null,
90
- quote: null,
91
- }
92
- }),
93
- assignSellAmount: assign(({ event }) => {
94
- const { amount, amountBaseUnit } = event as {
95
- type: 'SET_SELL_AMOUNT'
96
- amount: string
97
- amountBaseUnit: string | undefined
98
- }
99
- return {
100
- sellAmount: amount,
101
- sellAmountBaseUnit: amountBaseUnit,
102
- }
103
- }),
104
- assignSlippage: assign(({ event }) => ({
105
- slippage: (event as { type: 'SET_SLIPPAGE'; slippage: string }).slippage,
106
- })),
107
- assignSelectedRate: assign(({ event }) => ({
108
- selectedRate: (event as { type: 'SELECT_RATE'; rate: TradeRate }).rate,
109
- })),
110
- assignQuote: assign(({ event }) => ({
111
- quote: (event as { type: 'QUOTE_SUCCESS'; quote: QuoteResponse }).quote,
112
- })),
113
- assignQuoteError: assign(({ event }) => ({
114
- error: (event as { type: 'QUOTE_ERROR'; error: string }).error,
115
- errorSource: 'QUOTE_ERROR' as const,
116
- })),
117
- assignApprovalTxHash: assign(({ event }) => ({
118
- approvalTxHash: (event as { type: 'APPROVAL_SUCCESS'; txHash: string }).txHash,
119
- })),
120
- assignApprovalError: assign(({ event }) => ({
121
- error: (event as { type: 'APPROVAL_ERROR'; error: string }).error,
122
- errorSource: 'APPROVAL_ERROR' as const,
123
- })),
124
- assignTxHash: assign(({ event }) => ({
125
- txHash: (event as { type: 'EXECUTE_SUCCESS'; txHash: string }).txHash,
126
- })),
127
- assignExecuteError: assign(({ event }) => ({
128
- error: (event as { type: 'EXECUTE_ERROR'; error: string }).error,
129
- errorSource: 'EXECUTE_ERROR' as const,
130
- })),
131
- assignStatusFailed: assign(({ event }) => ({
132
- error: (event as { type: 'STATUS_FAILED'; error: string }).error,
133
- errorSource: 'STATUS_FAILED' as const,
134
- })),
135
- assignWalletAddress: assign(({ event }) => ({
136
- walletAddress: (event as { type: 'SET_WALLET_ADDRESS'; address: string | undefined }).address,
137
- })),
138
- assignReceiveAddress: assign(({ event }) => ({
139
- effectiveReceiveAddress: (event as { type: 'SET_RECEIVE_ADDRESS'; address: string }).address,
140
- })),
141
- assignChainInfo: assign(({ event }) => {
142
- const e = event as Extract<SwapMachineEvent, { type: 'UPDATE_CHAIN_INFO' }>
143
- return {
144
- chainType: e.chainType,
145
- isSellAssetEvm: e.isSellAssetEvm,
146
- isSellAssetUtxo: e.isSellAssetUtxo,
147
- isSellAssetSolana: e.isSellAssetSolana,
148
- isBuyAssetEvm: e.isBuyAssetEvm,
149
- }
150
- }),
151
- incrementRetryCount: assign(({ context }) => ({
152
- retryCount: context.retryCount + 1,
153
- error: null,
154
- errorSource: null,
155
- })),
156
- resetSwapState: assign(({ context }) => ({
157
- quote: null,
158
- txHash: null,
159
- approvalTxHash: null,
160
- error: null,
161
- errorSource: null,
162
- retryCount: 0,
163
- selectedRate: null,
164
- sellAsset: context.sellAsset,
165
- buyAsset: context.buyAsset,
166
- sellAmount: context.sellAmount,
167
- sellAmountBaseUnit: context.sellAmountBaseUnit,
168
- slippage: context.slippage,
169
- walletAddress: context.walletAddress,
170
- effectiveReceiveAddress: context.effectiveReceiveAddress,
171
- })),
172
- },
173
- }).createMachine({
174
- id: 'swap',
175
- initial: 'idle',
176
- context: createInitialContext(),
177
- states: {
178
- idle: {
179
- always: { target: 'input' },
180
- },
181
- input: {
182
- on: {
183
- SET_SELL_ASSET: { actions: 'assignSellAsset' },
184
- SET_BUY_ASSET: { actions: 'assignBuyAsset' },
185
- SET_SELL_AMOUNT: { actions: 'assignSellAmount' },
186
- SET_SLIPPAGE: { actions: 'assignSlippage' },
187
- SELECT_RATE: { actions: 'assignSelectedRate' },
188
- SET_WALLET_ADDRESS: { actions: 'assignWalletAddress' },
189
- SET_RECEIVE_ADDRESS: { actions: 'assignReceiveAddress' },
190
- UPDATE_CHAIN_INFO: { actions: 'assignChainInfo' },
191
- FETCH_QUOTE: {
192
- target: 'quoting',
193
- guard: 'hasValidInput',
194
- },
195
- },
196
- },
197
- quoting: {
198
- on: {
199
- QUOTE_SUCCESS: [
200
- {
201
- target: 'approval_needed',
202
- guard: 'isApprovalRequired',
203
- actions: 'assignQuote',
204
- },
205
- {
206
- target: 'executing',
207
- actions: 'assignQuote',
208
- },
209
- ],
210
- QUOTE_ERROR: {
211
- target: 'error',
212
- actions: 'assignQuoteError',
213
- },
214
- },
215
- },
216
- approval_needed: {
217
- on: {
218
- APPROVE: { target: 'approving' },
219
- RESET: { target: 'input', actions: 'resetSwapState' },
220
- },
221
- },
222
- approving: {
223
- on: {
224
- APPROVAL_SUCCESS: {
225
- target: 'executing',
226
- actions: 'assignApprovalTxHash',
227
- },
228
- APPROVAL_ERROR: {
229
- target: 'error',
230
- actions: 'assignApprovalError',
231
- },
232
- },
233
- },
234
- executing: {
235
- on: {
236
- EXECUTE_SUCCESS: {
237
- target: 'polling_status',
238
- actions: 'assignTxHash',
239
- },
240
- EXECUTE_ERROR: {
241
- target: 'error',
242
- actions: 'assignExecuteError',
243
- },
244
- },
245
- },
246
- polling_status: {
247
- on: {
248
- STATUS_CONFIRMED: { target: 'complete' },
249
- STATUS_FAILED: {
250
- target: 'error',
251
- actions: 'assignStatusFailed',
252
- },
253
- },
254
- },
255
- complete: {
256
- on: {
257
- RESET: { target: 'input', actions: 'resetSwapState' },
258
- },
259
- },
260
- error: {
261
- on: {
262
- RETRY: [
263
- {
264
- target: 'quoting',
265
- guard: { type: 'isQuoteError' },
266
- actions: 'incrementRetryCount',
267
- },
268
- {
269
- target: 'approving',
270
- guard: { type: 'isApprovalError' },
271
- actions: 'incrementRetryCount',
272
- },
273
- {
274
- target: 'quoting',
275
- guard: { type: 'isStatusFailed' },
276
- actions: 'incrementRetryCount',
277
- },
278
- {
279
- target: 'executing',
280
- guard: { type: 'canRetry' },
281
- actions: 'incrementRetryCount',
282
- },
283
- ],
284
- RESET: { target: 'input', actions: 'resetSwapState' },
285
- },
286
- },
287
- },
288
- })
@@ -1,54 +0,0 @@
1
- import type { Asset, QuoteResponse, TradeRate } from '../types'
2
-
3
- export type ErrorSource = 'QUOTE_ERROR' | 'APPROVAL_ERROR' | 'EXECUTE_ERROR' | 'STATUS_FAILED'
4
-
5
- export type SwapMachineContext = {
6
- sellAsset: Asset
7
- buyAsset: Asset
8
- sellAmount: string
9
- sellAmountBaseUnit: string | undefined
10
- selectedRate: TradeRate | null
11
- quote: QuoteResponse | null
12
- txHash: string | null
13
- approvalTxHash: string | null
14
- error: string | null
15
- errorSource: ErrorSource | null
16
- retryCount: number
17
- chainType: 'evm' | 'utxo' | 'solana' | 'cosmos' | 'other'
18
- slippage: string
19
- walletAddress: string | undefined
20
- effectiveReceiveAddress: string
21
- isSellAssetEvm: boolean
22
- isSellAssetUtxo: boolean
23
- isSellAssetSolana: boolean
24
- isBuyAssetEvm: boolean
25
- }
26
-
27
- export type SwapMachineEvent =
28
- | { type: 'SET_SELL_ASSET'; asset: Asset }
29
- | { type: 'SET_BUY_ASSET'; asset: Asset }
30
- | { type: 'SET_SELL_AMOUNT'; amount: string; amountBaseUnit: string | undefined }
31
- | { type: 'SET_SLIPPAGE'; slippage: string }
32
- | { type: 'SELECT_RATE'; rate: TradeRate }
33
- | { type: 'FETCH_QUOTE' }
34
- | { type: 'QUOTE_SUCCESS'; quote: QuoteResponse }
35
- | { type: 'QUOTE_ERROR'; error: string }
36
- | { type: 'APPROVE' }
37
- | { type: 'APPROVAL_SUCCESS'; txHash: string }
38
- | { type: 'APPROVAL_ERROR'; error: string }
39
- | { type: 'EXECUTE_SUCCESS'; txHash: string }
40
- | { type: 'EXECUTE_ERROR'; error: string }
41
- | { type: 'STATUS_CONFIRMED' }
42
- | { type: 'STATUS_FAILED'; error: string }
43
- | { type: 'RETRY' }
44
- | { type: 'RESET' }
45
- | { type: 'SET_WALLET_ADDRESS'; address: string | undefined }
46
- | { type: 'SET_RECEIVE_ADDRESS'; address: string }
47
- | {
48
- type: 'UPDATE_CHAIN_INFO'
49
- chainType: SwapMachineContext['chainType']
50
- isSellAssetEvm: boolean
51
- isSellAssetUtxo: boolean
52
- isSellAssetSolana: boolean
53
- isBuyAssetEvm: boolean
54
- }
@@ -1,322 +0,0 @@
1
- import type { Connection, SignatureStatus } from '@solana/web3.js'
2
- import type { PublicClient } from 'viem'
3
- import { createPublicClient, http } from 'viem'
4
- import { arbitrum, avalanche, base, bsc, gnosis, mainnet, optimism, polygon } from 'viem/chains'
5
-
6
- export type TransactionStatus = 'pending' | 'confirmed' | 'failed'
7
-
8
- export type TransactionStatusResult = {
9
- status: TransactionStatus
10
- confirmations?: number
11
- blockNumber?: number
12
- error?: string
13
- }
14
-
15
- export type BitcoinTransactionStatus = {
16
- confirmed: boolean
17
- block_height?: number
18
- block_hash?: string
19
- block_time?: number
20
- }
21
-
22
- type EvmChain =
23
- | typeof mainnet
24
- | typeof optimism
25
- | typeof bsc
26
- | typeof gnosis
27
- | typeof polygon
28
- | typeof base
29
- | typeof arbitrum
30
- | typeof avalanche
31
-
32
- const EVM_CHAINS_BY_ID: Record<number, EvmChain> = {
33
- 1: mainnet,
34
- 10: optimism,
35
- 56: bsc,
36
- 100: gnosis,
37
- 137: polygon,
38
- 8453: base,
39
- 42161: arbitrum,
40
- 43114: avalanche,
41
- }
42
-
43
- const MEMPOOL_API_BASE = 'https://mempool.space/api'
44
-
45
- export const checkEvmStatus = async (
46
- txHash: string,
47
- chainId: number,
48
- existingClient?: PublicClient,
49
- ): Promise<TransactionStatusResult> => {
50
- try {
51
- const chain = EVM_CHAINS_BY_ID[chainId]
52
- const client =
53
- existingClient ??
54
- createPublicClient({
55
- chain: chain ?? mainnet,
56
- transport: http(),
57
- })
58
-
59
- const receipt = await client.getTransactionReceipt({
60
- hash: txHash as `0x${string}`,
61
- })
62
-
63
- if (!receipt) {
64
- return { status: 'pending' }
65
- }
66
-
67
- const currentBlock = await client.getBlockNumber()
68
- const confirmations = Number(currentBlock - receipt.blockNumber) + 1
69
-
70
- if (receipt.status === 'success') {
71
- return {
72
- status: 'confirmed',
73
- confirmations,
74
- blockNumber: Number(receipt.blockNumber),
75
- }
76
- }
77
-
78
- return {
79
- status: 'failed',
80
- blockNumber: Number(receipt.blockNumber),
81
- error: 'Transaction reverted',
82
- }
83
- } catch (error) {
84
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
85
-
86
- if (
87
- errorMessage.includes('could not be found') ||
88
- errorMessage.includes('Transaction not found')
89
- ) {
90
- return { status: 'pending' }
91
- }
92
-
93
- return {
94
- status: 'pending',
95
- error: errorMessage,
96
- }
97
- }
98
- }
99
-
100
- export const checkBitcoinStatus = async (
101
- txid: string,
102
- network: 'mainnet' | 'testnet' = 'mainnet',
103
- ): Promise<TransactionStatusResult> => {
104
- try {
105
- const baseUrl = network === 'testnet' ? 'https://mempool.space/testnet/api' : MEMPOOL_API_BASE
106
-
107
- const response = await fetch(`${baseUrl}/tx/${txid}/status`)
108
-
109
- if (!response.ok) {
110
- if (response.status === 404) {
111
- return { status: 'pending' }
112
- }
113
- throw new Error(`HTTP error: ${response.status}`)
114
- }
115
-
116
- const data: BitcoinTransactionStatus = await response.json()
117
-
118
- if (data.confirmed) {
119
- const currentBlockResponse = await fetch(`${baseUrl}/blocks/tip/height`)
120
- const currentBlockHeight = await currentBlockResponse.json()
121
-
122
- const confirmations = data.block_height
123
- ? Number(currentBlockHeight) - data.block_height + 1
124
- : 1
125
-
126
- return {
127
- status: 'confirmed',
128
- confirmations,
129
- blockNumber: data.block_height,
130
- }
131
- }
132
-
133
- return { status: 'pending' }
134
- } catch (error) {
135
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
136
- return {
137
- status: 'pending',
138
- error: errorMessage,
139
- }
140
- }
141
- }
142
-
143
- export const checkSolanaStatus = async (
144
- signature: string,
145
- connection: Connection,
146
- ): Promise<TransactionStatusResult> => {
147
- try {
148
- const statusResult = await connection.getSignatureStatus(signature, {
149
- searchTransactionHistory: true,
150
- })
151
-
152
- const status: SignatureStatus | null = statusResult?.value
153
-
154
- if (!status) {
155
- return { status: 'pending' }
156
- }
157
-
158
- if (status.err) {
159
- return {
160
- status: 'failed',
161
- error: typeof status.err === 'string' ? status.err : JSON.stringify(status.err),
162
- }
163
- }
164
-
165
- if (status.confirmationStatus === 'finalized' || status.confirmationStatus === 'confirmed') {
166
- return {
167
- status: 'confirmed',
168
- confirmations: status.confirmations ?? undefined,
169
- blockNumber: status.slot,
170
- }
171
- }
172
-
173
- if (status.confirmationStatus === 'processed') {
174
- return {
175
- status: 'pending',
176
- confirmations: status.confirmations ?? undefined,
177
- blockNumber: status.slot,
178
- }
179
- }
180
-
181
- return { status: 'pending' }
182
- } catch (error) {
183
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
184
- return {
185
- status: 'pending',
186
- error: errorMessage,
187
- }
188
- }
189
- }
190
-
191
- export const waitForEvmConfirmation = async (
192
- txHash: string,
193
- chainId: number,
194
- confirmations = 1,
195
- timeoutMs = 120000,
196
- ): Promise<TransactionStatusResult> => {
197
- const chain = EVM_CHAINS_BY_ID[chainId]
198
- const client = createPublicClient({
199
- chain: chain ?? mainnet,
200
- transport: http(),
201
- })
202
-
203
- const receipt = await client.waitForTransactionReceipt({
204
- hash: txHash as `0x${string}`,
205
- confirmations,
206
- timeout: timeoutMs,
207
- })
208
-
209
- if (receipt.status === 'success') {
210
- return {
211
- status: 'confirmed',
212
- confirmations,
213
- blockNumber: Number(receipt.blockNumber),
214
- }
215
- }
216
-
217
- return {
218
- status: 'failed',
219
- blockNumber: Number(receipt.blockNumber),
220
- error: 'Transaction reverted',
221
- }
222
- }
223
-
224
- export const waitForBitcoinConfirmation = async (
225
- txid: string,
226
- requiredConfirmations = 1,
227
- pollIntervalMs = 30000,
228
- timeoutMs = 600000,
229
- network: 'mainnet' | 'testnet' = 'mainnet',
230
- ): Promise<TransactionStatusResult> => {
231
- const startTime = Date.now()
232
-
233
- while (Date.now() - startTime < timeoutMs) {
234
- const result = await checkBitcoinStatus(txid, network)
235
-
236
- if (result.status === 'confirmed' && (result.confirmations ?? 0) >= requiredConfirmations) {
237
- return result
238
- }
239
-
240
- if (result.status === 'failed') {
241
- return result
242
- }
243
-
244
- await new Promise(resolve => setTimeout(resolve, pollIntervalMs))
245
- }
246
-
247
- return {
248
- status: 'pending',
249
- error: 'Timeout waiting for confirmation',
250
- }
251
- }
252
-
253
- export const waitForSolanaConfirmation = async (
254
- signature: string,
255
- connection: Connection,
256
- commitment: 'confirmed' | 'finalized' = 'confirmed',
257
- ): Promise<TransactionStatusResult> => {
258
- try {
259
- const latestBlockhash = await connection.getLatestBlockhash()
260
-
261
- await connection.confirmTransaction(
262
- {
263
- signature,
264
- blockhash: latestBlockhash.blockhash,
265
- lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
266
- },
267
- commitment,
268
- )
269
-
270
- return await checkSolanaStatus(signature, connection)
271
- } catch (error) {
272
- const errorMessage = error instanceof Error ? error.message : 'Unknown error'
273
-
274
- if (errorMessage.includes('block height exceeded')) {
275
- return {
276
- status: 'failed',
277
- error: 'Transaction expired',
278
- }
279
- }
280
-
281
- return {
282
- status: 'failed',
283
- error: errorMessage,
284
- }
285
- }
286
- }
287
-
288
- export type ChainType = 'evm' | 'utxo' | 'solana'
289
-
290
- export type CheckStatusParams = {
291
- txHash: string
292
- chainType: ChainType
293
- chainId?: number
294
- connection?: Connection
295
- network?: 'mainnet' | 'testnet'
296
- }
297
-
298
- export const checkTransactionStatus = async (
299
- params: CheckStatusParams,
300
- ): Promise<TransactionStatusResult> => {
301
- const { txHash, chainType, chainId, connection, network = 'mainnet' } = params
302
-
303
- switch (chainType) {
304
- case 'evm':
305
- if (!chainId) {
306
- throw new Error('chainId is required for EVM transactions')
307
- }
308
- return await checkEvmStatus(txHash, chainId)
309
-
310
- case 'utxo':
311
- return await checkBitcoinStatus(txHash, network)
312
-
313
- case 'solana':
314
- if (!connection) {
315
- throw new Error('connection is required for Solana transactions')
316
- }
317
- return await checkSolanaStatus(txHash, connection)
318
-
319
- default:
320
- throw new Error(`Unsupported chain type: ${chainType}`)
321
- }
322
- }