@ensofinance/checkout-widget 0.0.13 → 0.0.15

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 (143) hide show
  1. package/dist/checkout-widget.es.js +8422 -8104
  2. package/dist/checkout-widget.es.js.map +1 -1
  3. package/dist/checkout-widget.umd.js +31 -31
  4. package/dist/checkout-widget.umd.js.map +1 -1
  5. package/dist/index.d.ts +7 -0
  6. package/orval.config.ts +40 -6
  7. package/package.json +1 -1
  8. package/src/components/BridgeFee.tsx +3 -3
  9. package/src/components/Checkout.tsx +2 -2
  10. package/src/components/ExchangeConfirmSecurity.tsx +16 -3
  11. package/src/components/QuoteParameters.tsx +33 -18
  12. package/src/components/TransactionDetailRow.tsx +6 -5
  13. package/src/components/cards/AssetCard.tsx +2 -2
  14. package/src/components/cards/OptionCard.tsx +2 -0
  15. package/src/components/steps/ExchangeFlow.tsx +255 -98
  16. package/src/components/steps/FlowSelector.tsx +26 -10
  17. package/src/components/steps/WalletFlow/WalletAmountStep.tsx +22 -23
  18. package/src/components/steps/WalletFlow/WalletTokenStep.tsx +5 -3
  19. package/src/components/ui/index.tsx +0 -3
  20. package/src/enso-api/api.ts +144 -3
  21. package/src/enso-api/custom-instance.ts +1 -0
  22. package/src/enso-api/index.ts +29 -118
  23. package/src/enso-api/model/approveActionDto.ts +17 -0
  24. package/src/enso-api/model/approveArgsDto.ts +22 -0
  25. package/src/enso-api/model/approveArgsDtoAmount.ts +16 -0
  26. package/src/enso-api/model/balanceActionDto.ts +17 -0
  27. package/src/enso-api/model/balanceArgsDto.ts +15 -0
  28. package/src/enso-api/model/borrowActionDto.ts +18 -0
  29. package/src/enso-api/model/borrowArgsDto.ts +22 -0
  30. package/src/enso-api/model/borrowArgsDtoAmountOut.ts +16 -0
  31. package/src/enso-api/model/bridgeActionDto.ts +19 -0
  32. package/src/enso-api/model/bridgeArgsDto.ts +29 -0
  33. package/src/enso-api/model/bridgeArgsDtoAmountIn.ts +16 -0
  34. package/src/enso-api/model/bridgeArgsDtoCallbackItem.ts +64 -0
  35. package/src/enso-api/model/bundleControllerBundleShortcutTransactionBodyItem.ts +69 -0
  36. package/src/enso-api/model/callActionDto.ts +18 -0
  37. package/src/enso-api/model/callArgsDto.ts +29 -0
  38. package/src/enso-api/model/callArgsDtoArgsItem.ts +17 -0
  39. package/src/enso-api/model/callArgsDtoArgsItemAnyOf.ts +12 -0
  40. package/src/enso-api/model/callArgsDtoValue.ts +16 -0
  41. package/src/enso-api/model/callOutput.ts +15 -0
  42. package/src/enso-api/model/depositActionDto.ts +18 -0
  43. package/src/enso-api/model/depositArgsDto.ts +28 -0
  44. package/src/enso-api/model/depositArgsDtoAmountIn.ts +20 -0
  45. package/src/enso-api/model/depositArgsDtoAmountInOneOfItem.ts +13 -0
  46. package/src/enso-api/model/depositArgsDtoTokenIn.ts +15 -0
  47. package/src/enso-api/model/depositArgsDtoTokenOut.ts +15 -0
  48. package/src/enso-api/model/depositCLMMActionDto.ts +18 -0
  49. package/src/enso-api/model/depositCLMMArgsDto.ts +30 -0
  50. package/src/enso-api/model/depositCLMMArgsDtoAmountInItem.ts +17 -0
  51. package/src/enso-api/model/depositCLMMArgsDtoAmountInItemAnyOf.ts +12 -0
  52. package/src/enso-api/model/ensoFeeActionDto.ts +17 -0
  53. package/src/enso-api/model/ensoFeeArgsDto.ts +20 -0
  54. package/src/enso-api/model/ensoFeeArgsDtoAmount.ts +16 -0
  55. package/src/enso-api/model/feeActionDto.ts +17 -0
  56. package/src/enso-api/model/feeArgsDto.ts +22 -0
  57. package/src/enso-api/model/feeArgsDtoAmount.ts +16 -0
  58. package/src/enso-api/model/harvestActionDto.ts +19 -0
  59. package/src/enso-api/model/harvestArgsDto.ts +17 -0
  60. package/src/enso-api/model/index.ts +115 -4
  61. package/src/enso-api/model/mergeActionDto.ts +17 -0
  62. package/src/enso-api/model/mergeArgsDto.ts +22 -0
  63. package/src/enso-api/model/mergeArgsDtoAmountInItem.ts +13 -0
  64. package/src/enso-api/model/minAmountOutActionDto.ts +17 -0
  65. package/src/enso-api/model/minAmountOutArgsDto.ts +19 -0
  66. package/src/enso-api/model/minAmountOutArgsDtoAmountOut.ts +16 -0
  67. package/src/enso-api/model/minAmountOutArgsDtoMinAmountOut.ts +16 -0
  68. package/src/enso-api/model/multiDepositActionDto.ts +18 -0
  69. package/src/enso-api/model/multiDepositArgsDto.ts +24 -0
  70. package/src/enso-api/model/multiDepositArgsDtoAmountInItem.ts +17 -0
  71. package/src/enso-api/model/multiDepositArgsDtoAmountInItemAnyOf.ts +12 -0
  72. package/src/enso-api/model/multiOutSingleDepositActionDto.ts +18 -0
  73. package/src/enso-api/model/multiOutSingleDepositArgsDto.ts +24 -0
  74. package/src/enso-api/model/multiOutSingleDepositArgsDtoAmountIn.ts +16 -0
  75. package/src/enso-api/model/multiRedeemActionDto.ts +18 -0
  76. package/src/enso-api/model/multiRedeemArgs2Dto.ts +24 -0
  77. package/src/enso-api/model/multiRedeemArgs2DtoAmountIn.ts +16 -0
  78. package/src/enso-api/model/nontokenizedControllerRouteNontokenizedShorcutTransactionParams.ts +8 -0
  79. package/src/enso-api/model/nontokenizedControllerRouteNontokenizedShorcutTransactionRoutingStrategy.ts +1 -0
  80. package/src/enso-api/model/nontokenizedRouteShortcutTransaction.ts +31 -0
  81. package/src/enso-api/model/paymasterFeeActionDto.ts +17 -0
  82. package/src/enso-api/model/paymasterFeeArgsDto.ts +18 -0
  83. package/src/enso-api/model/paymasterFeeArgsDtoAmount.ts +16 -0
  84. package/src/enso-api/model/permitTransferFromActionDto.ts +18 -0
  85. package/src/enso-api/model/permitTransferFromArgsDto.ts +29 -0
  86. package/src/enso-api/model/permitTransferFromArgsDtoAmount.ts +20 -0
  87. package/src/enso-api/model/permitTransferFromArgsDtoAmountOneOfItem.ts +13 -0
  88. package/src/enso-api/model/permitTransferFromArgsDtoToken.ts +15 -0
  89. package/src/enso-api/model/positionModel.ts +14 -0
  90. package/src/enso-api/model/redeemActionDto.ts +18 -0
  91. package/src/enso-api/model/redeemArgsDto.ts +25 -0
  92. package/src/enso-api/model/redeemArgsDtoAmountIn.ts +16 -0
  93. package/src/enso-api/model/redeemArgsDtoTokenOut.ts +15 -0
  94. package/src/enso-api/model/redeemCLMMActionDto.ts +18 -0
  95. package/src/enso-api/model/redeemCLMMArgsDto.ts +24 -0
  96. package/src/enso-api/model/redeemCLMMArgsDtoLiquidity.ts +16 -0
  97. package/src/enso-api/model/repayActionDto.ts +18 -0
  98. package/src/enso-api/model/repayArgsDto.ts +22 -0
  99. package/src/enso-api/model/repayArgsDtoAmountIn.ts +16 -0
  100. package/src/enso-api/model/routeActionDto.ts +20 -0
  101. package/src/enso-api/model/routeArgsDto.ts +38 -0
  102. package/src/enso-api/model/routeArgsDtoAmountIn.ts +16 -0
  103. package/src/enso-api/model/singleDepositActionDto.ts +18 -0
  104. package/src/enso-api/model/singleDepositArgsDto.ts +24 -0
  105. package/src/enso-api/model/singleDepositArgsDtoAmountIn.ts +16 -0
  106. package/src/enso-api/model/singleRedeemActionDto.ts +18 -0
  107. package/src/enso-api/model/singleRedeemArgs2Dto.ts +24 -0
  108. package/src/enso-api/model/singleRedeemArgs2DtoAmountIn.ts +16 -0
  109. package/src/enso-api/model/slippageActionDto.ts +17 -0
  110. package/src/enso-api/model/slippageArgsDto.ts +18 -0
  111. package/src/enso-api/model/slippageArgsDtoAmountOut.ts +16 -0
  112. package/src/enso-api/model/splitActionDto.ts +17 -0
  113. package/src/enso-api/model/splitArgsDto.ts +22 -0
  114. package/src/enso-api/model/splitArgsDtoAmountIn.ts +16 -0
  115. package/src/enso-api/model/swapActionDto.ts +18 -0
  116. package/src/enso-api/model/swapArgsDto.ts +30 -0
  117. package/src/enso-api/model/swapArgsDtoAmountIn.ts +16 -0
  118. package/src/enso-api/model/tokenizedMultiDepositActionDto.ts +18 -0
  119. package/src/enso-api/model/tokenizedMultiDepositArgsDto.ts +24 -0
  120. package/src/enso-api/model/tokenizedMultiDepositArgsDtoAmountInItem.ts +17 -0
  121. package/src/enso-api/model/tokenizedMultiDepositArgsDtoAmountInItemAnyOf.ts +14 -0
  122. package/src/enso-api/model/tokenizedMultiRedeemActionDto.ts +18 -0
  123. package/src/enso-api/model/tokenizedMultiRedeemArgsDto.ts +24 -0
  124. package/src/enso-api/model/tokenizedMultiRedeemArgsDtoAmountIn.ts +16 -0
  125. package/src/enso-api/model/tokenizedSingleDepositActionDto.ts +18 -0
  126. package/src/enso-api/model/tokenizedSingleDepositArgsDto.ts +24 -0
  127. package/src/enso-api/model/tokenizedSingleDepositArgsDtoAmountIn.ts +16 -0
  128. package/src/enso-api/model/tokenizedSingleRedeemActionDto.ts +18 -0
  129. package/src/enso-api/model/tokenizedSingleRedeemArgsDto.ts +24 -0
  130. package/src/enso-api/model/tokenizedSingleRedeemArgsDtoAmountIn.ts +16 -0
  131. package/src/enso-api/model/tokensControllerTokensLiquidityType.ts +19 -0
  132. package/src/enso-api/model/tokensControllerTokensParams.ts +6 -0
  133. package/src/enso-api/model/transferActionDto.ts +18 -0
  134. package/src/enso-api/model/transferArgsDto.ts +22 -0
  135. package/src/enso-api/model/transferArgsDtoAmount.ts +16 -0
  136. package/src/enso-api/model/transferFromActionDto.ts +18 -0
  137. package/src/enso-api/model/transferFromArgsDto.ts +24 -0
  138. package/src/enso-api/model/transferFromArgsDtoAmount.ts +16 -0
  139. package/src/index.ts +1 -0
  140. package/src/store.ts +12 -5
  141. package/src/types/index.ts +9 -3
  142. package/src/util/common.tsx +2 -0
  143. package/src/util/enso-hooks.tsx +72 -11
@@ -53,31 +53,29 @@ import { ConfirmExchangeStep } from "../ExchangeConfirmSecurity";
53
53
 
54
54
  import SuccessIcon from "@/assets/success.svg";
55
55
  import FailIcon from "@/assets/fail.svg";
56
+ import { SupportedExchanges } from "@/types";
56
57
 
57
58
  const ENTRY_POINT_ADDRESS: `0x${string}` =
58
59
  "0x0000000071727de22e5e9d8baf0edac6f37da032";
59
60
 
60
- export enum ExchangeType {
61
- binance = "binance",
62
- delayed = "delayed",
63
- }
64
- export const ExchangeNames = {
65
- [ExchangeType.binance]: "Binance",
66
- [ExchangeType.delayed]: "Smart Account",
61
+ export const ExchangeToIntegrationType: Record<SupportedExchanges, string> = {
62
+ [SupportedExchanges.Binance]: "binanceInternationalDirect",
63
+ [SupportedExchanges.Kraken]: "krakenDirect",
64
+ [SupportedExchanges.Coinbase]: "coinbase",
65
+ [SupportedExchanges.Bybit]: "bybitDirect",
67
66
  };
68
67
 
69
- // // Styled components
70
- // export const BodyWrapper = chakra("div", {
71
- // base: {
72
- // display: "flex",
73
- // flexDirection: "column",
74
- // width: "100%",
75
- // justifyContent: "center",
76
- // alignItems: "center",
77
- // gap: "16px",
78
- // paddingTop: "16px",
79
- // },
80
- // });
68
+ // Map Mesh broker types to icon URLs
69
+ export const EXCHANGE_ICON_BY_TYPE: Record<string, string> = {
70
+ [ExchangeToIntegrationType[SupportedExchanges.Binance]]:
71
+ "https://assets.coingecko.com/markets/images/52/large/binance.jpg",
72
+ [ExchangeToIntegrationType[SupportedExchanges.Kraken]]:
73
+ "https://assets.coingecko.com/markets/images/29/large/kraken.jpg",
74
+ [ExchangeToIntegrationType[SupportedExchanges.Coinbase]]:
75
+ "https://assets.coingecko.com/markets/images/23/large/Coinbase_Coin_Primary.png",
76
+ [ExchangeToIntegrationType[SupportedExchanges.Bybit]]:
77
+ "https://assets.coingecko.com/markets/images/698/large/bybit_spot.png",
78
+ };
81
79
 
82
80
  // Types for Mesh Holdings API response
83
81
  interface CryptocurrencyPosition {
@@ -141,6 +139,7 @@ Withdrawal steps:
141
139
 
142
140
  export enum WithdrawalStep {
143
141
  CheckSessionKey,
142
+ ChooseExchange,
144
143
  ChooseExchangeAsset,
145
144
  ChooseBalanceAsset,
146
145
  ChooseAmount,
@@ -149,6 +148,7 @@ export enum WithdrawalStep {
149
148
  TrackUserOp,
150
149
  }
151
150
  const withdrawalSteps = [
151
+ WithdrawalStep.ChooseExchange,
152
152
  WithdrawalStep.CheckSessionKey,
153
153
  WithdrawalStep.ChooseExchangeAsset,
154
154
  WithdrawalStep.ChooseAmount,
@@ -160,7 +160,7 @@ const balanceSteps = [
160
160
  WithdrawalStep.ChooseAmount,
161
161
  WithdrawalStep.SignUserOp,
162
162
  ];
163
- const BINANCE_INTEGRATION_ID = "9226e5c2-ebc3-4fdd-94f6-ed52cdce1420";
163
+ // Integration details are fetched dynamically from Mesh API.
164
164
 
165
165
  // Mesh network IDs for EVM chains (from Mesh networks API)
166
166
  const MESH_NETWORK_IDS: { [chainId: number]: string } = {
@@ -184,20 +184,138 @@ const getNetworkId = (chainId: number): string => {
184
184
 
185
185
  const useHandleMeshAccessPayload = () => {
186
186
  const { setMeshAccessToken } = useAppStore();
187
- const deviceKey = useDeviceKey();
188
-
189
- return useCallback((accessTokenPayload: AccessTokenPayload) => {
190
- setMeshAccessToken(accessTokenPayload); // Persist access token and session id for future reloads
191
-
192
- sessionStorage.setItem(
193
- deviceKey,
194
- JSON.stringify({
195
- accessToken: accessTokenPayload.accessToken,
196
- sessionId: accessTokenPayload.content.sessionId,
197
- timestamp: Date.now(),
198
- }),
187
+ const deviceKey = useDeviceId();
188
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
189
+
190
+ return useCallback(
191
+ (accessTokenPayload: AccessTokenPayload, sessionId: string) => {
192
+ setMeshAccessToken(accessTokenPayload); // Persist access token and session id for future reloads
193
+
194
+ debugger;
195
+ sessionStorage.setItem(
196
+ `${deviceKey}:${selectedIntegration?.type}`,
197
+ JSON.stringify({
198
+ accessToken:
199
+ accessTokenPayload.accountTokens[0].accessToken,
200
+ sessionId,
201
+ timestamp: Date.now(),
202
+ }),
203
+ );
204
+ },
205
+ [deviceKey, selectedIntegration?.type],
206
+ );
207
+ };
208
+
209
+ type MeshIntegration = {
210
+ id: string;
211
+ type: string; // brokerType
212
+ name: string;
213
+ networks?: {
214
+ id: string;
215
+ nativeSymbol?: string;
216
+ name?: string;
217
+ chainId?: string | number;
218
+ }[];
219
+ };
220
+
221
+ const ChooseExchangeStep = ({
222
+ setStep,
223
+ }: {
224
+ setStep: (step: WithdrawalStep) => void;
225
+ }) => {
226
+ const { chainIdOut } = useAppStore();
227
+ const [integrations, setIntegrations] = useState<MeshIntegration[]>([]);
228
+ const [loading, setLoading] = useState(true);
229
+ const [error, setError] = useState<string | null>(null);
230
+ const setSelectedIntegration = useAppStore(
231
+ (state) => state.setSelectedIntegration,
232
+ );
233
+ const { enableExchange } = useContext(CheckoutContext);
234
+
235
+ useEffect(() => {
236
+ const fetchIntegrations = async () => {
237
+ try {
238
+ const res = await fetch(`${MESH_API_URL}/integrations`);
239
+ const data = await res.json();
240
+ const availableExchanges = enableExchange
241
+ .map((e) => ExchangeToIntegrationType[e])
242
+ .filter(Boolean);
243
+
244
+ // Filter integrations by current chain support
245
+ const filtered = data?.filter(
246
+ (i) =>
247
+ i.networks.some((n) => +n.chainId === chainIdOut) &&
248
+ availableExchanges.includes(i.type),
249
+ );
250
+
251
+ setIntegrations(filtered);
252
+ } catch (e) {
253
+ console.error("Failed to fetch integrations", e);
254
+ setError("Failed to load exchanges");
255
+ } finally {
256
+ setLoading(false);
257
+ }
258
+ };
259
+ fetchIntegrations();
260
+ }, [chainIdOut]);
261
+
262
+ if (loading)
263
+ return (
264
+ <Center>
265
+ <Spinner m={5} />
266
+ </Center>
199
267
  );
200
- }, []);
268
+ if (error)
269
+ return (
270
+ <BodyWrapper>
271
+ <Box p={5} color="red.500">
272
+ {error}
273
+ </Box>
274
+ </BodyWrapper>
275
+ );
276
+
277
+ return (
278
+ <BodyWrapper>
279
+ <Box mb={4} width="100%" textAlign="left">
280
+ <HeaderTitle>Choose Exchange</HeaderTitle>
281
+ </Box>
282
+
283
+ {integrations?.length > 0 ? (
284
+ <ListWrapper>
285
+ {integrations.map((integration) => (
286
+ <AssetCard
287
+ key={integration.id}
288
+ // chainId={chainIdOut || 1}
289
+ icon={EXCHANGE_ICON_BY_TYPE[integration.type]}
290
+ title={integration.name}
291
+ balance={""}
292
+ usdBalance=""
293
+ tag=""
294
+ loading={false}
295
+ selected={false}
296
+ onClick={() => {
297
+ setSelectedIntegration({
298
+ id: integration.id,
299
+ type: integration.type,
300
+ name: integration.name,
301
+ });
302
+ setStep(WithdrawalStep.CheckSessionKey);
303
+ }}
304
+ />
305
+ ))}
306
+ </ListWrapper>
307
+ ) : (
308
+ <Box
309
+ textAlign="center"
310
+ fontSize={"sm"}
311
+ color="fg.subtle"
312
+ py={3}
313
+ >
314
+ No exchanges available for specified target chain
315
+ </Box>
316
+ )}
317
+ </BodyWrapper>
318
+ );
201
319
  };
202
320
 
203
321
  const CheckSessionKeyStep = ({
@@ -208,26 +326,30 @@ const CheckSessionKeyStep = ({
208
326
  const { chainIdOut, setMeshAccessToken, setSessionId, setChainIdIn } =
209
327
  useAppStore();
210
328
  const { address } = useAccount();
211
- const deviceKey = useDeviceKey();
329
+ const deviceKey = useDeviceId();
212
330
  const [showConfirmation, setShowConfirmation] = useState(false);
331
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
213
332
 
214
333
  const invalidChainId = chainIdOut && !MESH_NETWORKS.includes(chainIdOut);
215
334
  const handleMeshAccessPayload = useHandleMeshAccessPayload();
216
- const setActiveExchange = useAppStore((state) => state.setActiveExchange);
217
-
218
- useEffect(() => {
219
- setActiveExchange("binance");
220
- }, []);
221
335
 
222
336
  useEffect(() => {
223
337
  setChainIdIn(chainIdOut);
224
338
  }, [chainIdOut]);
225
339
 
226
340
  useEffect(() => {
341
+ if (!selectedIntegration) {
342
+ // ensure an exchange is selected
343
+ setStep(WithdrawalStep.ChooseExchange);
344
+ return;
345
+ }
227
346
  if (invalidChainId) return;
228
347
  // If connection is persisted, skip fetching a new link token
229
- const saved = sessionStorage.getItem(deviceKey);
348
+ const saved = sessionStorage.getItem(
349
+ `${deviceKey}:${selectedIntegration.type}`,
350
+ );
230
351
  // On load: check for persisted connection and hydrate state
352
+ debugger;
231
353
  if (saved) {
232
354
  try {
233
355
  const parsed = JSON.parse(saved);
@@ -245,20 +367,24 @@ const CheckSessionKeyStep = ({
245
367
 
246
368
  // Show confirmation instead of auto-connecting
247
369
  setShowConfirmation(true);
248
- }, [deviceKey, invalidChainId]);
370
+ }, [deviceKey, invalidChainId, selectedIntegration]);
249
371
 
250
372
  const handleConfirmAuth = () => {
251
- fetch(`${MESH_API_URL}/linktoken`, {
252
- method: "POST",
253
- headers: {
254
- "Content-Type": "application/json",
373
+ const brokerType = selectedIntegration?.type;
374
+ fetch(
375
+ `${MESH_API_URL}/linktoken?brokerType=${encodeURIComponent(brokerType)}`,
376
+ {
377
+ method: "POST",
378
+ headers: {
379
+ "Content-Type": "application/json",
380
+ },
381
+ body: JSON.stringify({
382
+ userId: deviceKey,
383
+ integrationId: selectedIntegration?.id,
384
+ address,
385
+ }),
255
386
  },
256
- body: JSON.stringify({
257
- userId: deviceKey,
258
- integrationId: BINANCE_INTEGRATION_ID,
259
- address,
260
- }),
261
- })
387
+ )
262
388
  .then((response) => response.json())
263
389
  .then((response) => {
264
390
  setSessionId(response.content.sessionId);
@@ -269,7 +395,10 @@ const CheckSessionKeyStep = ({
269
395
  console.log("onIntegrationConnected", payload);
270
396
  meshLink.closeLink();
271
397
  setStep(WithdrawalStep.ChooseExchangeAsset);
272
- handleMeshAccessPayload(payload.accessToken); // Persist access token and session id for future reloads
398
+ handleMeshAccessPayload(
399
+ payload.accessToken,
400
+ response.content.sessionId,
401
+ ); // Persist access token and session id for future reloads
273
402
  },
274
403
  onExit: (error) => {
275
404
  console.log("onExit", error);
@@ -310,21 +439,32 @@ const CheckSessionKeyStep = ({
310
439
  }
311
440
 
312
441
  if (showConfirmation) {
313
- return <ConfirmExchangeStep onConfirm={handleConfirmAuth} />;
442
+ return (
443
+ <ConfirmExchangeStep
444
+ onConfirm={handleConfirmAuth}
445
+ exchangeName={selectedIntegration?.name}
446
+ exchangeIcon={
447
+ selectedIntegration?.type
448
+ ? EXCHANGE_ICON_BY_TYPE[selectedIntegration.type]
449
+ : undefined
450
+ }
451
+ />
452
+ );
314
453
  }
315
454
 
316
455
  return <Spinner m={5} />;
317
456
  };
318
457
 
458
+ const DEVICE_ID_KEY = "meshDeviceId";
319
459
  // Generate a unique device ID to use as user id for Mesh
320
- const useDeviceKey = () => {
460
+ const useDeviceId = () => {
321
461
  return useMemo(() => {
322
- const deviceIdKey = "meshDeviceId";
323
- let deviceId = localStorage.getItem(deviceIdKey);
462
+ let deviceId = localStorage.getItem(DEVICE_ID_KEY);
463
+ console.log(deviceId);
324
464
 
325
465
  if (!deviceId) {
326
466
  deviceId = `device_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
327
- localStorage.setItem(deviceIdKey, deviceId);
467
+ localStorage.setItem(DEVICE_ID_KEY, deviceId);
328
468
  }
329
469
 
330
470
  return deviceId;
@@ -358,12 +498,14 @@ const ChooseAssetStep = ({
358
498
  setTokenIn,
359
499
  chainIdIn,
360
500
  } = useAppStore();
501
+ const deviceKey = useDeviceId();
502
+
503
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
361
504
 
362
- const deviceKey = useDeviceKey();
363
505
  useEffect(() => {
364
506
  const fetchData = async () => {
365
507
  try {
366
- // Fetch holdings from Binance
508
+ // Fetch holdings for the selected exchange
367
509
  const holdingsResponse = await fetch(
368
510
  `${MESH_API_URL}/holdings`,
369
511
  {
@@ -373,9 +515,8 @@ const ChooseAssetStep = ({
373
515
  "x-session-id": sessionId,
374
516
  },
375
517
  body: JSON.stringify({
376
- authToken:
377
- meshAccessToken.accountTokens[0].accessToken,
378
- brokerType: "binanceInternationalDirect",
518
+ authToken: meshAccessToken,
519
+ brokerType: selectedIntegration?.type,
379
520
  }),
380
521
  },
381
522
  );
@@ -393,7 +534,9 @@ const ChooseAssetStep = ({
393
534
  setStep(WithdrawalStep.CheckSessionKey);
394
535
  setMeshAccessToken(null);
395
536
  setSessionId(null);
396
- sessionStorage.removeItem(deviceKey);
537
+ sessionStorage.removeItem(
538
+ `${deviceKey}:${selectedIntegration?.type}`,
539
+ );
397
540
  }
398
541
  throw new Error(
399
542
  holdingsData.message || "Failed to fetch holdings",
@@ -402,10 +545,9 @@ const ChooseAssetStep = ({
402
545
 
403
546
  // Fetch supported tokens for current chain
404
547
  const tokensResponse = await fetch(
405
- `${MESH_API_URL}/tokens?chainId=${chainIdIn}`,
548
+ `${MESH_API_URL}/tokens?chainId=${chainIdIn}&brokerType=${encodeURIComponent(selectedIntegration?.type || "")}`,
406
549
  );
407
550
  const tokensData = await tokensResponse.json();
408
- console.log("Tokens data:", tokensData);
409
551
 
410
552
  if (
411
553
  tokensData.status === "success" &&
@@ -456,18 +598,16 @@ const ChooseAssetStep = ({
456
598
  }
457
599
  };
458
600
 
459
- if (meshAccessToken && sessionId && chainIdOut) {
601
+ if (meshAccessToken && sessionId && chainIdOut && selectedIntegration) {
460
602
  fetchData();
461
603
  }
462
- }, [address, chainIdOut, meshAccessToken, sessionId]);
604
+ }, [address, chainIdOut, meshAccessToken, sessionId, selectedIntegration]);
463
605
 
464
606
  const geckoTokens = useTokenFromListBySymbols(
465
607
  matchedTokens.map((token) => token.symbol),
466
608
  chainIdIn,
467
609
  );
468
610
 
469
- console.log("geckoTokens", geckoTokens);
470
-
471
611
  if (loading)
472
612
  return (
473
613
  <Center>
@@ -545,10 +685,16 @@ const ChooseDelayedBalance = ({
545
685
 
546
686
  // Get smart account balances
547
687
  const { holdingsList, total, isLoading } = useSmartAccountBalances(1);
548
- const setActiveExchange = useAppStore((state) => state.setActiveExchange);
688
+ const setSelectedIntegration = useAppStore(
689
+ (state) => state.setSelectedIntegration,
690
+ );
549
691
 
550
692
  useEffect(() => {
551
- setActiveExchange("delayed");
693
+ setSelectedIntegration({
694
+ type: "delayed",
695
+ name: "Smart account",
696
+ id: "",
697
+ });
552
698
  }, []);
553
699
 
554
700
  if (isLoading) {
@@ -637,7 +783,7 @@ const ChooseAmountStep = ({
637
783
  const [inputMode, setInputMode] = useState<"usd" | "token">("usd");
638
784
  const [usdValue, setUsdValue] = useState<string>("");
639
785
  const { setAmountIn } = useAppStore();
640
- const { tokenInData, tokenIn } = useAppDetails();
786
+ const { tokenInData } = useAppDetails();
641
787
  const isStable = selectedToken?.symbol.toLowerCase().includes("USD");
642
788
  const roundingPrecision = isStable ? 2 : 6;
643
789
 
@@ -1016,10 +1162,12 @@ const InitiateWithdrawalStep = ({
1016
1162
  const { meshAccessToken, amountIn, chainIdOut } = useAppStore();
1017
1163
  const { address } = useAccount();
1018
1164
  const { tokenInData } = useAppDetails();
1165
+ const sessionId = useAppStore((state) => state.sessionId);
1019
1166
  const [isLoading, setIsLoading] = useState(true);
1167
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
1020
1168
 
1021
1169
  const handleMeshAccessPayload = useHandleMeshAccessPayload();
1022
- const deviceKey = useDeviceKey();
1170
+ const deviceKey = useDeviceId();
1023
1171
 
1024
1172
  useEffect(() => {
1025
1173
  if (!selectedToken || !userOp || !meshAccessToken) {
@@ -1047,7 +1195,7 @@ const InitiateWithdrawalStep = ({
1047
1195
  const meshData = {
1048
1196
  restrictMultipleAccounts: true,
1049
1197
  userId: deviceKey,
1050
- integrationId: BINANCE_INTEGRATION_ID,
1198
+ integrationId: selectedIntegration?.id,
1051
1199
  transferOptions: {
1052
1200
  toAddresses,
1053
1201
  },
@@ -1055,31 +1203,35 @@ const InitiateWithdrawalStep = ({
1055
1203
 
1056
1204
  console.log("link request body", meshData);
1057
1205
 
1058
- const response = await fetch(`${MESH_API_URL}/linktoken`, {
1059
- method: "POST",
1060
- headers: {
1061
- "Content-Type": "application/json",
1062
- },
1063
- body: JSON.stringify({
1064
- restrictMultipleAccounts: true,
1065
- userId: deviceKey,
1066
- integrationId: BINANCE_INTEGRATION_ID,
1067
- transferOptions: {
1068
- toAddresses,
1206
+ const response = await fetch(
1207
+ `${MESH_API_URL}/linktoken?brokerType=${encodeURIComponent(selectedIntegration?.type || "")}`,
1208
+ {
1209
+ method: "POST",
1210
+ headers: {
1211
+ "Content-Type": "application/json",
1069
1212
  },
1070
- }),
1071
- });
1213
+ body: JSON.stringify({
1214
+ restrictMultipleAccounts: true,
1215
+ userId: deviceKey,
1216
+ integrationId: selectedIntegration?.id,
1217
+ transferOptions: {
1218
+ toAddresses,
1219
+ },
1220
+ }),
1221
+ },
1222
+ );
1072
1223
 
1073
1224
  const data = await response.json();
1074
1225
  console.log("Link token response:", data);
1075
1226
 
1227
+ // @ts-ignore
1076
1228
  const accessTokens: IntegrationAccessToken[] =
1077
1229
  meshAccessToken.accountTokens.map((token) => ({
1078
1230
  ...token,
1079
1231
  accountId: token.account.accountId,
1080
1232
  accountName: token.account.accountName,
1081
- brokerName: "Binance",
1082
- brokerType: "binanceInternationalDirect",
1233
+ brokerName: selectedIntegration?.name,
1234
+ brokerType: selectedIntegration?.type,
1083
1235
  }));
1084
1236
 
1085
1237
  console.log("accessTokens", accessTokens);
@@ -1088,7 +1240,7 @@ const InitiateWithdrawalStep = ({
1088
1240
  clientId: address,
1089
1241
  accessTokens,
1090
1242
  onIntegrationConnected: (payload) => {
1091
- handleMeshAccessPayload(payload.accessToken); // Persist access token and session id for future reloads
1243
+ handleMeshAccessPayload(payload.accessToken, sessionId); // Persist access token and session id for future reloads
1092
1244
  console.log("Integration connected", payload);
1093
1245
  },
1094
1246
  onTransferFinished: (transferData) => {
@@ -1441,7 +1593,7 @@ const TrackUserOpStep = ({
1441
1593
 
1442
1594
  const ExchangeFlow = ({
1443
1595
  setFlow,
1444
- initialStep = WithdrawalStep.CheckSessionKey,
1596
+ initialStep = WithdrawalStep.ChooseExchange,
1445
1597
  }: {
1446
1598
  setFlow: (string) => void;
1447
1599
  initialStep?: WithdrawalStep;
@@ -1452,15 +1604,19 @@ const ExchangeFlow = ({
1452
1604
  null,
1453
1605
  );
1454
1606
  const [userOp, setUserOp] = useState<any | null>(null);
1455
- const setActiveExchange = useAppStore((state) => state.setActiveExchange);
1456
- const activeExchange = useAppStore((state) => state.activeExchange);
1607
+ const setSelectedIntegration = useAppStore(
1608
+ (state) => state.setSelectedIntegration,
1609
+ );
1610
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
1457
1611
 
1458
1612
  useEffect(() => {
1459
- return () => setActiveExchange("");
1613
+ return () => setSelectedIntegration(null);
1460
1614
  }, []);
1461
1615
 
1462
1616
  const currentStepComponent = (() => {
1463
1617
  switch (currentStep) {
1618
+ case WithdrawalStep.ChooseExchange:
1619
+ return <ChooseExchangeStep setStep={setCurrentStep} />;
1464
1620
  case WithdrawalStep.CheckSessionKey:
1465
1621
  return <CheckSessionKeyStep setStep={setCurrentStep} />;
1466
1622
  case WithdrawalStep.ChooseExchangeAsset:
@@ -1528,9 +1684,10 @@ const ExchangeFlow = ({
1528
1684
  maxWidth={"16px"}
1529
1685
  onClick={() => {
1530
1686
  const index =
1531
- withdrawalSteps.findIndex(
1532
- (step) => step === currentStep,
1533
- ) - 1;
1687
+ (selectedIntegration?.type === "delayed"
1688
+ ? balanceSteps
1689
+ : withdrawalSteps
1690
+ ).findIndex((step) => step === currentStep) - 1;
1534
1691
  if (index > 0) {
1535
1692
  setCurrentStep(withdrawalSteps[index]);
1536
1693
  } else {
@@ -1554,7 +1711,7 @@ const ExchangeFlow = ({
1554
1711
  width="100%"
1555
1712
  >
1556
1713
  <HeaderTitle>
1557
- Deposit from {ExchangeNames[activeExchange]}
1714
+ Deposit from {selectedIntegration?.name}
1558
1715
  </HeaderTitle>
1559
1716
  </Box>
1560
1717
 
@@ -17,18 +17,20 @@ import { CheckoutContext } from "../Checkout";
17
17
  import { WalletCard, OptionCard } from "../cards";
18
18
  import { WalletStatus } from "../cards/WalletCard";
19
19
  import { useWalletIcon, useSmartAccountBalances } from "@/util/enso-hooks";
20
- import ExchangeFlow, { WithdrawalStep } from "@/components/steps/ExchangeFlow";
20
+ import ExchangeFlow, {
21
+ WithdrawalStep,
22
+ EXCHANGE_ICON_BY_TYPE,
23
+ ExchangeToIntegrationType,
24
+ } from "@/components/steps/ExchangeFlow";
21
25
  import WalletFlow from "@/components/steps/WalletFlow/WalletFlow";
22
26
 
23
- import BinanceIcon from "../../assets/BinanceBadge.svg";
24
-
25
27
  const FLOWS = {
26
28
  exchangeFlow: ExchangeFlow,
27
29
  walletFlow: WalletFlow,
28
30
  };
29
31
 
30
32
  const FlowSelector = () => {
31
- const { handleClose, enableExchanges } = useContext(CheckoutContext);
33
+ const { handleClose, enableExchange } = useContext(CheckoutContext);
32
34
  const [flow, setFlow] = useState("");
33
35
  const [initialStep, setInitialStep] = useState<string | number>("");
34
36
 
@@ -102,19 +104,33 @@ const FlowSelector = () => {
102
104
  firstStep: WithdrawalStep.ChooseBalanceAsset,
103
105
  });
104
106
  }
105
-
106
- if (enableExchanges?.includes?.("binance"))
107
+ console.log(
108
+ enableExchange,
109
+ enableExchange.map(
110
+ (integration) => EXCHANGE_ICON_BY_TYPE[integration],
111
+ ),
112
+ );
113
+
114
+ if (Array.isArray(enableExchange) && enableExchange.length > 0)
107
115
  options.unshift({
108
- id: "binance-exchange",
116
+ id: "exchange",
109
117
  title: "Connect Exchange",
110
118
  limit: "No Limit",
111
119
  delay: "2 min",
112
- icons: [BinanceIcon],
120
+ icons: enableExchange
121
+ .map(
122
+ (integration) =>
123
+ EXCHANGE_ICON_BY_TYPE[
124
+ ExchangeToIntegrationType[integration]
125
+ ],
126
+ )
127
+ .filter(Boolean),
113
128
  flow: "exchangeFlow",
114
- firstStep: WithdrawalStep.CheckSessionKey,
129
+ // Start at ChooseExchange to allow picking among multiple integrations
130
+ firstStep: WithdrawalStep.ChooseExchange,
115
131
  });
116
132
  return options;
117
- }, [address, enableExchanges, smartAccountTotal, isLoadingSmartAccount]);
133
+ }, [address, enableExchange, smartAccountTotal, isLoadingSmartAccount]);
118
134
 
119
135
  const FlowComponent = flow && FLOWS[flow];
120
136