@ensofinance/checkout-widget 0.0.13 → 0.0.14

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 +8421 -8103
  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 +254 -96
  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
  {
@@ -375,7 +517,7 @@ const ChooseAssetStep = ({
375
517
  body: JSON.stringify({
376
518
  authToken:
377
519
  meshAccessToken.accountTokens[0].accessToken,
378
- brokerType: "binanceInternationalDirect",
520
+ brokerType: selectedIntegration?.type,
379
521
  }),
380
522
  },
381
523
  );
@@ -393,7 +535,9 @@ const ChooseAssetStep = ({
393
535
  setStep(WithdrawalStep.CheckSessionKey);
394
536
  setMeshAccessToken(null);
395
537
  setSessionId(null);
396
- sessionStorage.removeItem(deviceKey);
538
+ sessionStorage.removeItem(
539
+ `${deviceKey}:${selectedIntegration?.type}`,
540
+ );
397
541
  }
398
542
  throw new Error(
399
543
  holdingsData.message || "Failed to fetch holdings",
@@ -402,10 +546,9 @@ const ChooseAssetStep = ({
402
546
 
403
547
  // Fetch supported tokens for current chain
404
548
  const tokensResponse = await fetch(
405
- `${MESH_API_URL}/tokens?chainId=${chainIdIn}`,
549
+ `${MESH_API_URL}/tokens?chainId=${chainIdIn}&brokerType=${encodeURIComponent(selectedIntegration?.type || "")}`,
406
550
  );
407
551
  const tokensData = await tokensResponse.json();
408
- console.log("Tokens data:", tokensData);
409
552
 
410
553
  if (
411
554
  tokensData.status === "success" &&
@@ -456,18 +599,16 @@ const ChooseAssetStep = ({
456
599
  }
457
600
  };
458
601
 
459
- if (meshAccessToken && sessionId && chainIdOut) {
602
+ if (meshAccessToken && sessionId && chainIdOut && selectedIntegration) {
460
603
  fetchData();
461
604
  }
462
- }, [address, chainIdOut, meshAccessToken, sessionId]);
605
+ }, [address, chainIdOut, meshAccessToken, sessionId, selectedIntegration]);
463
606
 
464
607
  const geckoTokens = useTokenFromListBySymbols(
465
608
  matchedTokens.map((token) => token.symbol),
466
609
  chainIdIn,
467
610
  );
468
611
 
469
- console.log("geckoTokens", geckoTokens);
470
-
471
612
  if (loading)
472
613
  return (
473
614
  <Center>
@@ -545,10 +686,16 @@ const ChooseDelayedBalance = ({
545
686
 
546
687
  // Get smart account balances
547
688
  const { holdingsList, total, isLoading } = useSmartAccountBalances(1);
548
- const setActiveExchange = useAppStore((state) => state.setActiveExchange);
689
+ const setSelectedIntegration = useAppStore(
690
+ (state) => state.setSelectedIntegration,
691
+ );
549
692
 
550
693
  useEffect(() => {
551
- setActiveExchange("delayed");
694
+ setSelectedIntegration({
695
+ type: "delayed",
696
+ name: "Smart account",
697
+ id: "",
698
+ });
552
699
  }, []);
553
700
 
554
701
  if (isLoading) {
@@ -637,7 +784,7 @@ const ChooseAmountStep = ({
637
784
  const [inputMode, setInputMode] = useState<"usd" | "token">("usd");
638
785
  const [usdValue, setUsdValue] = useState<string>("");
639
786
  const { setAmountIn } = useAppStore();
640
- const { tokenInData, tokenIn } = useAppDetails();
787
+ const { tokenInData } = useAppDetails();
641
788
  const isStable = selectedToken?.symbol.toLowerCase().includes("USD");
642
789
  const roundingPrecision = isStable ? 2 : 6;
643
790
 
@@ -1016,10 +1163,12 @@ const InitiateWithdrawalStep = ({
1016
1163
  const { meshAccessToken, amountIn, chainIdOut } = useAppStore();
1017
1164
  const { address } = useAccount();
1018
1165
  const { tokenInData } = useAppDetails();
1166
+ const sessionId = useAppStore((state) => state.sessionId);
1019
1167
  const [isLoading, setIsLoading] = useState(true);
1168
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
1020
1169
 
1021
1170
  const handleMeshAccessPayload = useHandleMeshAccessPayload();
1022
- const deviceKey = useDeviceKey();
1171
+ const deviceKey = useDeviceId();
1023
1172
 
1024
1173
  useEffect(() => {
1025
1174
  if (!selectedToken || !userOp || !meshAccessToken) {
@@ -1047,7 +1196,7 @@ const InitiateWithdrawalStep = ({
1047
1196
  const meshData = {
1048
1197
  restrictMultipleAccounts: true,
1049
1198
  userId: deviceKey,
1050
- integrationId: BINANCE_INTEGRATION_ID,
1199
+ integrationId: selectedIntegration?.id,
1051
1200
  transferOptions: {
1052
1201
  toAddresses,
1053
1202
  },
@@ -1055,31 +1204,35 @@ const InitiateWithdrawalStep = ({
1055
1204
 
1056
1205
  console.log("link request body", meshData);
1057
1206
 
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,
1207
+ const response = await fetch(
1208
+ `${MESH_API_URL}/linktoken?brokerType=${encodeURIComponent(selectedIntegration?.type || "")}`,
1209
+ {
1210
+ method: "POST",
1211
+ headers: {
1212
+ "Content-Type": "application/json",
1069
1213
  },
1070
- }),
1071
- });
1214
+ body: JSON.stringify({
1215
+ restrictMultipleAccounts: true,
1216
+ userId: deviceKey,
1217
+ integrationId: selectedIntegration?.id,
1218
+ transferOptions: {
1219
+ toAddresses,
1220
+ },
1221
+ }),
1222
+ },
1223
+ );
1072
1224
 
1073
1225
  const data = await response.json();
1074
1226
  console.log("Link token response:", data);
1075
1227
 
1228
+ // @ts-ignore
1076
1229
  const accessTokens: IntegrationAccessToken[] =
1077
1230
  meshAccessToken.accountTokens.map((token) => ({
1078
1231
  ...token,
1079
1232
  accountId: token.account.accountId,
1080
1233
  accountName: token.account.accountName,
1081
- brokerName: "Binance",
1082
- brokerType: "binanceInternationalDirect",
1234
+ brokerName: selectedIntegration?.name,
1235
+ brokerType: selectedIntegration?.type,
1083
1236
  }));
1084
1237
 
1085
1238
  console.log("accessTokens", accessTokens);
@@ -1088,7 +1241,7 @@ const InitiateWithdrawalStep = ({
1088
1241
  clientId: address,
1089
1242
  accessTokens,
1090
1243
  onIntegrationConnected: (payload) => {
1091
- handleMeshAccessPayload(payload.accessToken); // Persist access token and session id for future reloads
1244
+ handleMeshAccessPayload(payload.accessToken, sessionId); // Persist access token and session id for future reloads
1092
1245
  console.log("Integration connected", payload);
1093
1246
  },
1094
1247
  onTransferFinished: (transferData) => {
@@ -1441,7 +1594,7 @@ const TrackUserOpStep = ({
1441
1594
 
1442
1595
  const ExchangeFlow = ({
1443
1596
  setFlow,
1444
- initialStep = WithdrawalStep.CheckSessionKey,
1597
+ initialStep = WithdrawalStep.ChooseExchange,
1445
1598
  }: {
1446
1599
  setFlow: (string) => void;
1447
1600
  initialStep?: WithdrawalStep;
@@ -1452,15 +1605,19 @@ const ExchangeFlow = ({
1452
1605
  null,
1453
1606
  );
1454
1607
  const [userOp, setUserOp] = useState<any | null>(null);
1455
- const setActiveExchange = useAppStore((state) => state.setActiveExchange);
1456
- const activeExchange = useAppStore((state) => state.activeExchange);
1608
+ const setSelectedIntegration = useAppStore(
1609
+ (state) => state.setSelectedIntegration,
1610
+ );
1611
+ const selectedIntegration = useAppStore((s) => s.selectedIntegration);
1457
1612
 
1458
1613
  useEffect(() => {
1459
- return () => setActiveExchange("");
1614
+ return () => setSelectedIntegration(null);
1460
1615
  }, []);
1461
1616
 
1462
1617
  const currentStepComponent = (() => {
1463
1618
  switch (currentStep) {
1619
+ case WithdrawalStep.ChooseExchange:
1620
+ return <ChooseExchangeStep setStep={setCurrentStep} />;
1464
1621
  case WithdrawalStep.CheckSessionKey:
1465
1622
  return <CheckSessionKeyStep setStep={setCurrentStep} />;
1466
1623
  case WithdrawalStep.ChooseExchangeAsset:
@@ -1528,9 +1685,10 @@ const ExchangeFlow = ({
1528
1685
  maxWidth={"16px"}
1529
1686
  onClick={() => {
1530
1687
  const index =
1531
- withdrawalSteps.findIndex(
1532
- (step) => step === currentStep,
1533
- ) - 1;
1688
+ (selectedIntegration?.type === "delayed"
1689
+ ? balanceSteps
1690
+ : withdrawalSteps
1691
+ ).findIndex((step) => step === currentStep) - 1;
1534
1692
  if (index > 0) {
1535
1693
  setCurrentStep(withdrawalSteps[index]);
1536
1694
  } else {
@@ -1554,7 +1712,7 @@ const ExchangeFlow = ({
1554
1712
  width="100%"
1555
1713
  >
1556
1714
  <HeaderTitle>
1557
- Deposit from {ExchangeNames[activeExchange]}
1715
+ Deposit from {selectedIntegration?.name}
1558
1716
  </HeaderTitle>
1559
1717
  </Box>
1560
1718
 
@@ -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