@lifi/widget 3.6.0-alpha.5 → 3.6.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 (190) hide show
  1. package/App.tsx +3 -3
  2. package/AppProvider.tsx +2 -1
  3. package/CHANGELOG.md +16 -0
  4. package/_esm/App.d.ts +1 -1
  5. package/_esm/App.js +3 -3
  6. package/_esm/App.js.map +1 -1
  7. package/_esm/AppProvider.js +2 -2
  8. package/_esm/AppProvider.js.map +1 -1
  9. package/_esm/components/Card/InputCard.d.ts +1 -1
  10. package/_esm/components/ChainSelect/useChainSelect.js +1 -5
  11. package/_esm/components/ChainSelect/useChainSelect.js.map +1 -1
  12. package/_esm/components/FeeBreakdownTooltip.js +1 -4
  13. package/_esm/components/FeeBreakdownTooltip.js.map +1 -1
  14. package/_esm/components/Header/EVMDisconnectIconButton.js +4 -5
  15. package/_esm/components/Header/EVMDisconnectIconButton.js.map +1 -1
  16. package/_esm/components/Header/Header.style.d.ts +0 -3
  17. package/_esm/components/Header/Header.style.js +0 -5
  18. package/_esm/components/Header/Header.style.js.map +1 -1
  19. package/_esm/components/Header/WalletMenu.js +6 -5
  20. package/_esm/components/Header/WalletMenu.js.map +1 -1
  21. package/_esm/components/Header/WalletMenu.style.js +1 -5
  22. package/_esm/components/Header/WalletMenu.style.js.map +1 -1
  23. package/_esm/components/SendToWallet/SendToWalletButton.js +16 -6
  24. package/_esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
  25. package/_esm/components/SendToWallet/SendToWalletExpandButton.js +8 -6
  26. package/_esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
  27. package/_esm/components/Skeleton/WidgetSkeleton.style.d.ts +1 -1
  28. package/_esm/components/Step/StepProcess.js +9 -1
  29. package/_esm/components/Step/StepProcess.js.map +1 -1
  30. package/_esm/components/TokenList/TokenListItem.js +5 -11
  31. package/_esm/components/TokenList/TokenListItem.js.map +1 -1
  32. package/_esm/components/TokenList/VirtualizedTokenList.js +1 -1
  33. package/_esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  34. package/_esm/components/TokenList/types.d.ts +2 -2
  35. package/_esm/components/TokenRate/TokenRate.js +2 -2
  36. package/_esm/components/TokenRate/TokenRate.js.map +1 -1
  37. package/_esm/components/TransactionDetails.js +2 -1
  38. package/_esm/components/TransactionDetails.js.map +1 -1
  39. package/_esm/config/version.d.ts +1 -1
  40. package/_esm/config/version.js +1 -1
  41. package/_esm/config/version.js.map +1 -1
  42. package/_esm/hooks/useAccount.d.ts +15 -15
  43. package/_esm/hooks/useAccount.js +13 -50
  44. package/_esm/hooks/useAccount.js.map +1 -1
  45. package/_esm/hooks/useAvailableChains.d.ts +1 -2
  46. package/_esm/hooks/useAvailableChains.js +8 -9
  47. package/_esm/hooks/useAvailableChains.js.map +1 -1
  48. package/_esm/hooks/useChains.d.ts +3 -4
  49. package/_esm/hooks/useChains.js +3 -5
  50. package/_esm/hooks/useChains.js.map +1 -1
  51. package/_esm/hooks/useExplorer.d.ts +10 -1
  52. package/_esm/hooks/useExplorer.js +4 -1
  53. package/_esm/hooks/useExplorer.js.map +1 -1
  54. package/_esm/hooks/useGasSufficiency.js +2 -2
  55. package/_esm/hooks/useGasSufficiency.js.map +1 -1
  56. package/_esm/hooks/useHeaderHeight.js +8 -1
  57. package/_esm/hooks/useHeaderHeight.js.map +1 -1
  58. package/_esm/hooks/useTokens.js +1 -1
  59. package/_esm/hooks/useTokens.js.map +1 -1
  60. package/_esm/hooks/useWallets.d.ts +1 -1
  61. package/_esm/hooks/useWallets.js +15 -36
  62. package/_esm/hooks/useWallets.js.map +1 -1
  63. package/_esm/i18n/en.json +1 -1
  64. package/_esm/pages/SelectWalletPage/EVMListItemButton.d.ts +2 -1
  65. package/_esm/pages/SelectWalletPage/EVMListItemButton.js +14 -15
  66. package/_esm/pages/SelectWalletPage/EVMListItemButton.js.map +1 -1
  67. package/_esm/pages/SelectWalletPage/SVMListItemButton.js +0 -3
  68. package/_esm/pages/SelectWalletPage/SVMListItemButton.js.map +1 -1
  69. package/_esm/pages/SelectWalletPage/SelectWalletPage.js +3 -3
  70. package/_esm/pages/SelectWalletPage/SelectWalletPage.js.map +1 -1
  71. package/_esm/pages/SendToWallet/SendToWalletPage.style.d.ts +1 -1
  72. package/_esm/pages/TransactionDetailsPage/TransferIdCard.js +1 -1
  73. package/_esm/pages/TransactionDetailsPage/TransferIdCard.js.map +1 -1
  74. package/_esm/pages/TransactionPage/StatusBottomSheet.js +3 -2
  75. package/_esm/pages/TransactionPage/StatusBottomSheet.js.map +1 -1
  76. package/_esm/providers/I18nProvider/types.d.ts +2 -2
  77. package/_esm/providers/WalletProvider/SDKProviders.js +7 -15
  78. package/_esm/providers/WalletProvider/SDKProviders.js.map +1 -1
  79. package/_esm/providers/WalletProvider/WalletProvider.js +1 -2
  80. package/_esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  81. package/_esm/providers/WalletProvider/useHasExternalWalletProvider.d.ts +1 -1
  82. package/_esm/providers/WalletProvider/useHasExternalWalletProvider.js +3 -8
  83. package/_esm/providers/WalletProvider/useHasExternalWalletProvider.js.map +1 -1
  84. package/_esm/providers/WidgetProvider/WidgetProvider.js +1 -31
  85. package/_esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  86. package/_esm/stores/StoreProvider.js +2 -2
  87. package/_esm/stores/StoreProvider.js.map +1 -1
  88. package/_esm/stores/form/FormStore.d.ts +6 -1
  89. package/_esm/stores/form/FormStore.js +69 -18
  90. package/_esm/stores/form/FormStore.js.map +1 -1
  91. package/_esm/stores/form/FormUpdater.d.ts +1 -1
  92. package/_esm/stores/form/FormUpdater.js +36 -18
  93. package/_esm/stores/form/FormUpdater.js.map +1 -1
  94. package/_esm/stores/form/URLSearchParamsBuilder.js +44 -4
  95. package/_esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
  96. package/_esm/stores/form/createFormStore.js +9 -0
  97. package/_esm/stores/form/createFormStore.js.map +1 -1
  98. package/_esm/stores/form/types.d.ts +13 -10
  99. package/_esm/stores/form/types.js.map +1 -1
  100. package/_esm/stores/form/useFieldActions.js +1 -0
  101. package/_esm/stores/form/useFieldActions.js.map +1 -1
  102. package/_esm/stores/form/useFormRef.d.ts +3 -0
  103. package/_esm/stores/form/useFormRef.js +51 -0
  104. package/_esm/stores/form/useFormRef.js.map +1 -0
  105. package/_esm/stores/form/useTouchedFields.d.ts +6 -6
  106. package/_esm/stores/settings/types.d.ts +0 -2
  107. package/_esm/stores/settings/useSendToWalletStore.d.ts +0 -1
  108. package/_esm/stores/settings/useSendToWalletStore.js +0 -7
  109. package/_esm/stores/settings/useSendToWalletStore.js.map +1 -1
  110. package/_esm/themes/createTheme.js +1 -2
  111. package/_esm/themes/createTheme.js.map +1 -1
  112. package/_esm/types/widget.d.ts +29 -4
  113. package/_esm/types/widget.js.map +1 -1
  114. package/_esm/utils/chainType.js +3 -2
  115. package/_esm/utils/chainType.js.map +1 -1
  116. package/_esm/utils/fees.js +5 -9
  117. package/_esm/utils/fees.js.map +1 -1
  118. package/_esm/utils/svm.d.ts +1 -0
  119. package/_esm/utils/svm.js +11 -0
  120. package/_esm/utils/svm.js.map +1 -0
  121. package/components/ChainSelect/useChainSelect.ts +1 -10
  122. package/components/FeeBreakdownTooltip.tsx +1 -4
  123. package/components/Header/EVMDisconnectIconButton.tsx +5 -5
  124. package/components/Header/Header.style.ts +0 -6
  125. package/components/Header/WalletMenu.style.tsx +1 -5
  126. package/components/Header/WalletMenu.tsx +5 -6
  127. package/components/SendToWallet/SendToWalletButton.tsx +23 -13
  128. package/components/SendToWallet/SendToWalletExpandButton.tsx +10 -10
  129. package/components/Step/StepProcess.tsx +12 -2
  130. package/components/TokenList/TokenListItem.tsx +7 -15
  131. package/components/TokenList/VirtualizedTokenList.tsx +1 -1
  132. package/components/TokenList/types.ts +2 -2
  133. package/components/TokenRate/TokenRate.tsx +2 -2
  134. package/components/TransactionDetails.tsx +4 -1
  135. package/config/version.ts +1 -1
  136. package/hooks/useAccount.ts +35 -82
  137. package/hooks/useAvailableChains.ts +10 -9
  138. package/hooks/useChains.ts +3 -6
  139. package/hooks/useExplorer.ts +19 -2
  140. package/hooks/useGasSufficiency.ts +2 -4
  141. package/hooks/useHeaderHeight.ts +8 -1
  142. package/hooks/useTokens.ts +1 -2
  143. package/hooks/useWallets.ts +20 -41
  144. package/i18n/en.json +1 -1
  145. package/package.json +8 -8
  146. package/pages/SelectWalletPage/EVMListItemButton.tsx +19 -15
  147. package/pages/SelectWalletPage/SVMListItemButton.tsx +0 -3
  148. package/pages/SelectWalletPage/SelectWalletPage.tsx +4 -9
  149. package/pages/TransactionDetailsPage/TransferIdCard.tsx +1 -1
  150. package/pages/TransactionPage/StatusBottomSheet.tsx +5 -0
  151. package/providers/I18nProvider/types.ts +2 -2
  152. package/providers/WalletProvider/SDKProviders.tsx +7 -25
  153. package/providers/WalletProvider/WalletProvider.tsx +2 -5
  154. package/providers/WalletProvider/useHasExternalWalletProvider.ts +5 -11
  155. package/providers/WidgetProvider/WidgetProvider.tsx +1 -39
  156. package/stores/StoreProvider.tsx +5 -4
  157. package/stores/form/FormStore.tsx +100 -20
  158. package/stores/form/FormUpdater.tsx +52 -22
  159. package/stores/form/URLSearchParamsBuilder.tsx +55 -5
  160. package/stores/form/createFormStore.ts +9 -0
  161. package/stores/form/types.ts +14 -10
  162. package/stores/form/useFieldActions.ts +1 -0
  163. package/stores/form/useFormRef.ts +69 -0
  164. package/stores/settings/types.ts +0 -2
  165. package/stores/settings/useSendToWalletStore.ts +0 -8
  166. package/themes/createTheme.ts +1 -3
  167. package/types/widget.ts +46 -3
  168. package/utils/chainType.ts +3 -2
  169. package/utils/fees.ts +13 -15
  170. package/utils/svm.ts +10 -0
  171. package/_esm/components/Header/UTXODisconnectIconButton.d.ts +0 -4
  172. package/_esm/components/Header/UTXODisconnectIconButton.js +0 -13
  173. package/_esm/components/Header/UTXODisconnectIconButton.js.map +0 -1
  174. package/_esm/pages/SelectWalletPage/UTXOListItemButton.d.ts +0 -8
  175. package/_esm/pages/SelectWalletPage/UTXOListItemButton.js +0 -39
  176. package/_esm/pages/SelectWalletPage/UTXOListItemButton.js.map +0 -1
  177. package/_esm/providers/WalletProvider/UTXOBaseProvider.d.ts +0 -2
  178. package/_esm/providers/WalletProvider/UTXOBaseProvider.js +0 -17
  179. package/_esm/providers/WalletProvider/UTXOBaseProvider.js.map +0 -1
  180. package/_esm/providers/WalletProvider/UTXOExternalContext.d.ts +0 -1
  181. package/_esm/providers/WalletProvider/UTXOExternalContext.js +0 -3
  182. package/_esm/providers/WalletProvider/UTXOExternalContext.js.map +0 -1
  183. package/_esm/providers/WalletProvider/UTXOProvider.d.ts +0 -3
  184. package/_esm/providers/WalletProvider/UTXOProvider.js +0 -18
  185. package/_esm/providers/WalletProvider/UTXOProvider.js.map +0 -1
  186. package/components/Header/UTXODisconnectIconButton.tsx +0 -24
  187. package/pages/SelectWalletPage/UTXOListItemButton.tsx +0 -70
  188. package/providers/WalletProvider/UTXOBaseProvider.tsx +0 -28
  189. package/providers/WalletProvider/UTXOExternalContext.ts +0 -3
  190. package/providers/WalletProvider/UTXOProvider.tsx +0 -26
@@ -1,21 +1,30 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import { useEffect, useRef } from 'react';
2
+ import { useEffect } from 'react';
3
3
  import { useAccount } from '../../hooks/useAccount.js';
4
4
  import { useChains } from '../../hooks/useChains.js';
5
5
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
6
- import type { DefaultValues, FormFieldNames } from './types.js';
6
+ import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js';
7
+ import { formDefaultValues } from '../../stores/form/createFormStore.js';
8
+ import { useSendToWalletActions } from '../../stores/settings/useSendToWalletStore.js';
9
+ import type { DefaultValues } from './types.js';
7
10
  import { useFieldActions } from './useFieldActions.js';
8
11
 
9
12
  export const FormUpdater: React.FC<{
10
- defaultValues: Partial<DefaultValues>;
11
- }> = ({ defaultValues }) => {
12
- const { fromChain, toChain } = useWidgetConfig();
13
+ reactiveFormValues: Partial<DefaultValues>;
14
+ }> = ({ reactiveFormValues }) => {
15
+ const { fromChain, toChain, toAddress } = useWidgetConfig();
13
16
  const { account } = useAccount();
14
17
  const { chains } = useChains();
15
- const { isTouched, resetField, setFieldValue, getFieldValues } =
16
- useFieldActions();
17
- const previousDefaultValues = useRef(defaultValues);
18
+ const { setSendToWallet } = useSendToWalletActions();
19
+ const { setSelectedBookmark } = useBookmarkActions();
18
20
 
21
+ const {
22
+ isTouched,
23
+ resetField,
24
+ setFieldValue,
25
+ getFieldValues,
26
+ setUserAndDefaultValues,
27
+ } = useFieldActions();
19
28
  // Set wallet chain as default if no chains are provided by config and if they were not changed during widget usage
20
29
  useEffect(() => {
21
30
  const chainAllowed =
@@ -50,21 +59,42 @@ export const FormUpdater: React.FC<{
50
59
  // Makes widget config options reactive to changes
51
60
  // should update userValues when defaultValues updates and includes additional logic for chains
52
61
  useEffect(() => {
53
- (Object.keys(defaultValues) as FormFieldNames[]).forEach((key) => {
54
- if (previousDefaultValues.current[key] !== defaultValues[key]) {
55
- const value =
56
- defaultValues[key] ||
57
- // set the chain to the current user one if it is not present in the config
58
- (key === 'fromChain' || key === 'toChain'
59
- ? account.chainId || ''
60
- : '');
61
- setFieldValue(key, value);
62
- resetField(key, { defaultValue: value });
63
- }
64
- });
65
- previousDefaultValues.current = defaultValues;
62
+ if (reactiveFormValues.toAddress) {
63
+ setSendToWallet(true);
64
+ }
65
+
66
+ setSelectedBookmark(toAddress);
67
+
68
+ setUserAndDefaultValues(
69
+ accountForChainId(reactiveFormValues, account.chainId),
70
+ );
66
71
  // eslint-disable-next-line react-hooks/exhaustive-deps
67
- }, [defaultValues, getFieldValues, resetField, setFieldValue]);
72
+ }, [
73
+ toAddress,
74
+ reactiveFormValues,
75
+ getFieldValues,
76
+ resetField,
77
+ setFieldValue,
78
+ setUserAndDefaultValues,
79
+ setSendToWallet,
80
+ setSelectedBookmark,
81
+ ]);
68
82
 
69
83
  return null;
70
84
  };
85
+
86
+ const accountForChainId = (
87
+ defaultValues: Partial<DefaultValues>,
88
+ chainId?: number,
89
+ ) => {
90
+ const result: Partial<DefaultValues> = { ...defaultValues };
91
+ for (const key in result) {
92
+ const k = key as keyof DefaultValues;
93
+ if (result[k] === formDefaultValues[k]) {
94
+ if ((k === 'fromChain' || k === 'toChain') && chainId) {
95
+ result[k] = chainId;
96
+ }
97
+ }
98
+ }
99
+ return result;
100
+ };
@@ -1,6 +1,9 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useLocation } from 'react-router-dom';
3
- import type { FormFieldNames } from '../form/types.js';
3
+ import { useSendToWalletActions } from '../../stores/settings/useSendToWalletStore.js';
4
+ import { formatInputAmount } from '../../utils/format.js';
5
+ import type { DefaultValues, FormFieldNames } from '../form/types.js';
6
+ import { useFieldActions } from '../form/useFieldActions.js';
4
7
  import { useFieldValues } from '../form/useFieldValues.js';
5
8
  import { useTouchedFields } from '../form/useTouchedFields.js';
6
9
 
@@ -13,24 +16,71 @@ const formValueKeys: FormFieldNames[] = [
13
16
  'toToken',
14
17
  ];
15
18
 
19
+ const getDefaultValuesFromQueryString = (): Partial<DefaultValues> => {
20
+ const searchParams = Object.fromEntries(
21
+ new URLSearchParams(window?.location.search),
22
+ );
23
+
24
+ // Prevent using fromToken/toToken params if chain is not selected
25
+ ['from', 'to'].forEach((key) => {
26
+ if (searchParams[`${key}Token`] && !searchParams[`${key}Chain`]) {
27
+ delete searchParams[`${key}Token`];
28
+ }
29
+ });
30
+
31
+ return {
32
+ ...(Number.isFinite(parseInt(searchParams.fromChain, 10))
33
+ ? { fromChain: parseInt(searchParams.fromChain, 10) }
34
+ : {}),
35
+ ...(Number.isFinite(parseInt(searchParams.toChain, 10))
36
+ ? { toChain: parseInt(searchParams.toChain, 10) }
37
+ : {}),
38
+ ...(searchParams.fromToken ? { fromToken: searchParams.fromToken } : {}),
39
+ ...(searchParams.toToken ? { toToken: searchParams.toToken } : {}),
40
+ ...(Number.isFinite(parseFloat(searchParams.fromAmount))
41
+ ? { fromAmount: formatInputAmount(searchParams.fromAmount) }
42
+ : {}),
43
+ ...(searchParams.toAddress ? { toAddress: searchParams.toAddress } : {}),
44
+ };
45
+ };
46
+
16
47
  export const URLSearchParamsBuilder = () => {
17
48
  const { pathname } = useLocation();
18
49
  const touchedFields = useTouchedFields();
19
50
  const values = useFieldValues(...formValueKeys);
51
+ const { setSendToWallet } = useSendToWalletActions();
52
+
53
+ // Using these methods as trying to use the touchedFields and values above
54
+ // often has a lag that can effect the widgets initialisation sequence
55
+ // and accidentally cause values to be wiped from the query string
56
+ const { getFieldValues, isTouched, setUserAndDefaultValues } =
57
+ useFieldActions();
58
+
59
+ useEffect(() => {
60
+ // get the initial values from the querysting
61
+ const formValues = getDefaultValuesFromQueryString();
62
+
63
+ if (formValues.toAddress) {
64
+ setSendToWallet(true);
65
+ }
66
+
67
+ setUserAndDefaultValues(formValues);
68
+ }, [setUserAndDefaultValues, setSendToWallet]);
20
69
 
21
70
  useEffect(() => {
71
+ // set the values on the querysting
22
72
  const url = new URL(window.location as any);
23
73
  formValueKeys.forEach((key, index) => {
24
- const value = values[index];
25
- if (touchedFields[key] && value) {
74
+ const value = getFieldValues(key)[0];
75
+ if (isTouched(key) && value) {
26
76
  url.searchParams.set(key, value.toString());
27
- } else if (url.searchParams.has(key) && !values[index]) {
77
+ } else if (url.searchParams.has(key) && !value) {
28
78
  url.searchParams.delete(key);
29
79
  }
30
80
  });
31
81
  url.searchParams.sort();
32
82
  window.history.replaceState(window.history.state, '', url);
33
- }, [pathname, touchedFields, values]);
83
+ }, [pathname, touchedFields, values, isTouched, getFieldValues]);
34
84
 
35
85
  return null;
36
86
  };
@@ -94,6 +94,15 @@ export const createFormStore = (defaultValues?: DefaultValues) =>
94
94
  ),
95
95
  }));
96
96
  },
97
+ setUserAndDefaultValues: (formValues) => {
98
+ const currentUserValues = get().userValues;
99
+ (Object.keys(formValues) as FormFieldNames[]).forEach((key) => {
100
+ if (formValues[key] !== currentUserValues[key]?.value) {
101
+ get().resetField(key, { defaultValue: formValues[key] });
102
+ get().setFieldValue(key, formValues[key], { isTouched: true });
103
+ }
104
+ });
105
+ },
97
106
  isTouched: (fieldName: FormFieldNames) =>
98
107
  !!get().userValues[fieldName]?.isTouched,
99
108
  setAsTouched: (fieldName: FormFieldNames) => {
@@ -2,15 +2,18 @@ import type { ContractCall } from '@lifi/sdk';
2
2
  import type { StoreApi } from 'zustand';
3
3
  import type { UseBoundStoreWithEqualityFn } from 'zustand/traditional';
4
4
 
5
- export interface DefaultValues {
6
- contractCalls?: ContractCall[];
7
- fromAmount: string;
5
+ export interface DefaultFieldValues {
8
6
  fromChain?: number;
9
7
  fromToken?: string;
10
- toAddress?: string;
11
- toAmount: string;
12
8
  toChain?: number;
13
9
  toToken?: string;
10
+ toAddress?: string;
11
+ fromAmount: string;
12
+ toAmount: string;
13
+ }
14
+
15
+ export interface DefaultValues extends DefaultFieldValues {
16
+ contractCalls?: ContractCall[];
14
17
  tokenSearchFilter: string;
15
18
  }
16
19
 
@@ -32,17 +35,17 @@ export type FormFieldArray<T extends FormFieldNames[]> = {
32
35
  [K in keyof T]: ExtractValueType<FormValues[T[K]]>;
33
36
  };
34
37
 
35
- export type TouchedFields = { [K in FormFieldNames]?: boolean };
38
+ export type TouchedFields = { [key in FormFieldNames]?: boolean };
36
39
 
37
40
  type ValidationFn = (value: any) => Promise<boolean | string>;
38
41
  export interface ValidationProps {
39
42
  isValid: boolean;
40
43
  isValidating: boolean;
41
44
  errors: {
42
- [K in FormFieldNames]?: string;
45
+ [key in FormFieldNames]?: string;
43
46
  };
44
47
  validation: {
45
- [K in FormFieldNames]?: ValidationFn;
48
+ [key in FormFieldNames]?: ValidationFn;
46
49
  };
47
50
  }
48
51
 
@@ -58,7 +61,7 @@ export interface ValidationActions {
58
61
  export interface FormProps {
59
62
  defaultValues: FormValues;
60
63
  userValues: FormValues;
61
- touchedFields: { [K in FormFieldNames]?: boolean };
64
+ touchedFields: { [key in FormFieldNames]?: boolean };
62
65
  }
63
66
 
64
67
  interface ResetOptions {
@@ -67,6 +70,7 @@ interface ResetOptions {
67
70
 
68
71
  export interface FormActions {
69
72
  setDefaultValues: (formValues: DefaultValues) => void;
73
+ setUserAndDefaultValues: (formValues: Partial<DefaultValues>) => void;
70
74
  isTouched: (fieldName: FormFieldNames) => boolean;
71
75
  setAsTouched: (fieldName: FormFieldNames) => void;
72
76
  resetField: (fieldName: FormFieldNames, resetOptions?: ResetOptions) => void;
@@ -89,7 +93,7 @@ export type FormStoreStore = UseBoundStoreWithEqualityFn<
89
93
  StoreApi<FormValuesState>
90
94
  >;
91
95
 
92
- interface SetOptions {
96
+ export interface SetOptions {
93
97
  isDirty?: boolean;
94
98
  isTouched?: boolean;
95
99
  }
@@ -11,6 +11,7 @@ export const useFieldActions = () => {
11
11
  setAsTouched: store.setAsTouched,
12
12
  setDefaultValues: store.setDefaultValues,
13
13
  setFieldValue: store.setFieldValue,
14
+ setUserAndDefaultValues: store.setUserAndDefaultValues,
14
15
  }),
15
16
  shallow,
16
17
  );
@@ -0,0 +1,69 @@
1
+ import { useImperativeHandle } from 'react';
2
+ import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
3
+ import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js';
4
+ import { formDefaultValues } from '../../stores/form/createFormStore.js';
5
+ import { useSendToWalletActions } from '../../stores/settings/useSendToWalletStore.js';
6
+ import type { FormRef } from '../../types/widget.js';
7
+ import { HiddenUI } from '../../types/widget.js';
8
+ import type { FormStoreStore, GenericFormValue } from './types.js';
9
+
10
+ export const useFormRef = (formStore: FormStoreStore, formRef?: FormRef) => {
11
+ const { setSendToWallet } = useSendToWalletActions();
12
+ const { setSelectedBookmark } = useBookmarkActions();
13
+ const { hiddenUI } = useWidgetConfig();
14
+
15
+ useImperativeHandle(formRef, () => {
16
+ const sanitizeValue: {
17
+ [key: string]: (value: any) => GenericFormValue;
18
+ } = {
19
+ fromAmount: (value) =>
20
+ (typeof value === 'number' ? value?.toPrecision() : value) ||
21
+ formDefaultValues.fromAmount,
22
+ toAmount: (value) =>
23
+ (typeof value === 'number' ? value?.toPrecision() : value) ||
24
+ formDefaultValues.toAmount,
25
+ toAddress: (value) => {
26
+ if (hiddenUI?.includes(HiddenUI.ToAddress)) {
27
+ return formDefaultValues.toAddress;
28
+ }
29
+
30
+ const isToAddressObj = typeof value !== 'string';
31
+
32
+ const address =
33
+ (isToAddressObj ? value?.address : value) ||
34
+ formDefaultValues.toAddress;
35
+
36
+ // sets the send to wallet button state to be open
37
+ // if there is an address to display
38
+ if (address) {
39
+ setSendToWallet(address);
40
+ }
41
+
42
+ // we can assume that the toAddress has been passed as ToAddress object
43
+ // and display it accordingly - this ensures that if a name is included
44
+ // that it is displayed in the Send To Wallet form field correctly
45
+ if (isToAddressObj) {
46
+ setSelectedBookmark(value);
47
+ }
48
+
49
+ return address;
50
+ },
51
+ };
52
+
53
+ return {
54
+ setFieldValue: (fieldName, value, options) => {
55
+ const sanitizedValue = (
56
+ sanitizeValue[fieldName] ? sanitizeValue[fieldName](value) : value
57
+ ) as GenericFormValue;
58
+
59
+ const fieldValueOptions = options?.setUrlSearchParam
60
+ ? { isTouched: options?.setUrlSearchParam }
61
+ : undefined;
62
+
63
+ formStore
64
+ .getState()
65
+ .setFieldValue(fieldName, sanitizedValue, fieldValueOptions);
66
+ },
67
+ };
68
+ }, [formStore, hiddenUI, setSendToWallet, setSelectedBookmark]);
69
+ };
@@ -46,11 +46,9 @@ export interface SettingsState extends SettingsProps {
46
46
 
47
47
  export interface SendToWalletState {
48
48
  showSendToWallet: boolean;
49
- showSendToWalletDirty: boolean;
50
49
  }
51
50
 
52
51
  export interface SendToWalletStore extends SendToWalletState {
53
- toggleSendToWallet(): void;
54
52
  setSendToWallet(value: boolean): void;
55
53
  }
56
54
 
@@ -5,16 +5,9 @@ import type { SendToWalletStore } from './types.js';
5
5
  export const useSendToWalletStore = createWithEqualityFn<SendToWalletStore>(
6
6
  (set) => ({
7
7
  showSendToWallet: false,
8
- showSendToWalletDirty: false,
9
- toggleSendToWallet: () =>
10
- set((state) => ({
11
- showSendToWallet: !state.showSendToWallet,
12
- showSendToWalletDirty: true,
13
- })),
14
8
  setSendToWallet: (value) =>
15
9
  set({
16
10
  showSendToWallet: value,
17
- showSendToWalletDirty: true,
18
11
  }),
19
12
  }),
20
13
  Object.is,
@@ -23,7 +16,6 @@ export const useSendToWalletStore = createWithEqualityFn<SendToWalletStore>(
23
16
  export const useSendToWalletActions = () => {
24
17
  const actions = useSendToWalletStore(
25
18
  (store) => ({
26
- toggleSendToWallet: store.toggleSendToWallet,
27
19
  setSendToWallet: store.setSendToWallet,
28
20
  }),
29
21
  shallow,
@@ -63,7 +63,7 @@ export const createTheme = (
63
63
  const borderRadiusSecondary =
64
64
  widgetTheme.shape?.borderRadiusSecondary ?? shape.borderRadiusSecondary;
65
65
 
66
- const theme = createMuiTheme({
66
+ return createMuiTheme({
67
67
  container: widgetTheme.container,
68
68
  header: widgetTheme.header,
69
69
  navigation: {
@@ -409,6 +409,4 @@ export const createTheme = (
409
409
  },
410
410
  },
411
411
  });
412
-
413
- return theme;
414
412
  };
package/types/widget.ts CHANGED
@@ -3,6 +3,7 @@ import type {
3
3
  ChainType,
4
4
  ContractCall,
5
5
  Order,
6
+ RouteExtended,
6
7
  RouteOptions,
7
8
  SDKConfig,
8
9
  StaticToken,
@@ -21,11 +22,18 @@ import type {
21
22
  MetaMaskParameters,
22
23
  WalletConnectParameters,
23
24
  } from '@wagmi/connectors';
24
- import type { CSSProperties, ReactNode, RefObject } from 'react';
25
+ import type {
26
+ CSSProperties,
27
+ FC,
28
+ MutableRefObject,
29
+ ReactNode,
30
+ RefObject,
31
+ } from 'react';
25
32
  import type {
26
33
  LanguageKey,
27
34
  LanguageResources,
28
35
  } from '../providers/I18nProvider/types.js';
36
+ import type { DefaultFieldValues } from '../stores/form/types.js';
29
37
 
30
38
  export type WidgetVariant = 'compact' | 'wide' | 'drawer';
31
39
  export type WidgetSubvariant = 'default' | 'split' | 'custom' | 'refuel';
@@ -143,6 +151,10 @@ export interface WidgetFeeConfig {
143
151
  * @returns A promise that resolves to the calculated fee as a number (e.g., 0.03 represents a 3% fee)
144
152
  */
145
153
  calculateFee?(params: CalculateFeeParams): Promise<number | undefined>;
154
+ /**
155
+ * @internal
156
+ */
157
+ _vcComponent: FC<{ route: RouteExtended }>;
146
158
  }
147
159
 
148
160
  export interface ToAddress {
@@ -182,6 +194,7 @@ export interface WidgetConfig {
182
194
  toAddresses?: ToAddress[];
183
195
  fromAmount?: number | string;
184
196
  toAmount?: number | string;
197
+ formUpdateKey?: string;
185
198
 
186
199
  contractCalls?: ContractCall[];
187
200
  contractComponent?: ReactNode;
@@ -228,7 +241,36 @@ export interface WidgetConfig {
228
241
  Partial<Record<'internal', string[]>>;
229
242
  }
230
243
 
231
- export interface WidgetConfigProps {
244
+ export interface FormFieldOptions {
245
+ setUrlSearchParam: boolean;
246
+ }
247
+
248
+ export interface FieldValues
249
+ extends Omit<DefaultFieldValues, 'fromAmount' | 'toAmount' | 'toAddress'> {
250
+ fromAmount?: number | string;
251
+ toAmount?: number | string;
252
+ toAddress?: ToAddress | string;
253
+ }
254
+
255
+ export type FieldNames = keyof FieldValues;
256
+
257
+ export type SetFieldValueFunction = <K extends FieldNames>(
258
+ key: K,
259
+ value: FieldValues[K],
260
+ options?: FormFieldOptions,
261
+ ) => void;
262
+
263
+ export type FormState = {
264
+ setFieldValue: SetFieldValueFunction;
265
+ };
266
+
267
+ export type FormRef = MutableRefObject<FormState | null>;
268
+
269
+ export interface FormRefProps {
270
+ formRef?: FormRef;
271
+ }
272
+
273
+ export interface WidgetConfigProps extends FormRefProps {
232
274
  config: WidgetConfig;
233
275
  }
234
276
 
@@ -238,7 +280,8 @@ export interface WidgetConfigPartialProps {
238
280
 
239
281
  export type WidgetProps = WidgetDrawerProps &
240
282
  WidgetConfig &
241
- WidgetConfigPartialProps;
283
+ WidgetConfigPartialProps &
284
+ FormRefProps;
242
285
 
243
286
  export interface WidgetDrawerProps extends WidgetConfigPartialProps {
244
287
  elementRef?: RefObject<HTMLDivElement>;
@@ -1,10 +1,11 @@
1
- import { ChainId, ChainType, isSVMAddress, isUTXOAddress } from '@lifi/sdk';
1
+ import { ChainId, ChainType } from '@lifi/sdk';
2
2
  import { isAddress as isEVMAddress } from 'viem';
3
+ import { isSVMAddress } from './svm.js';
3
4
 
4
5
  const chainTypeAddressValidation = {
5
6
  [ChainType.EVM]: isEVMAddress,
6
7
  [ChainType.SVM]: isSVMAddress,
7
- [ChainType.UTXO]: isUTXOAddress,
8
+ [ChainType.UTXO]: () => false,
8
9
  };
9
10
 
10
11
  export const getChainTypeFromAddress = (
package/utils/fees.ts CHANGED
@@ -104,22 +104,20 @@ export const getFeeCostsBreakdown = (
104
104
  export const getStepFeeCostsBreakdown = (
105
105
  feeCosts: FeeCost[] | GasCost[],
106
106
  ): FeesBreakdown => {
107
- const { token } = feeCosts[0];
108
-
109
- const { amount, amountUSD } = feeCosts.reduce(
110
- (acc, feeCost) => {
111
- const feeAmount = BigInt(Number(feeCost.amount).toFixed(0) || 0);
112
- const amountUSD =
113
- parseFloat(feeCost.token.priceUSD || '0') *
114
- parseFloat(formatUnits(feeAmount, feeCost.token.decimals));
115
-
116
- acc.amount += feeAmount;
117
- acc.amountUSD += amountUSD;
118
- return acc;
119
- },
120
- { amount: 0n, amountUSD: 0 },
107
+ const token = feeCosts[0].token;
108
+ const amount = feeCosts.reduce(
109
+ (amount, feeCost) => amount + BigInt(feeCost.amount || 0),
110
+ 0n,
111
+ );
112
+ const amountUSD = feeCosts.reduce(
113
+ (amount, feeCost) =>
114
+ amount +
115
+ parseFloat(feeCost.token.priceUSD || '0') *
116
+ parseFloat(
117
+ formatUnits(BigInt(feeCost.amount || 0), feeCost.token.decimals),
118
+ ),
119
+ 0,
121
120
  );
122
-
123
121
  return {
124
122
  amount,
125
123
  amountUSD,
package/utils/svm.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
3
+ export const isSVMAddress = (address: string) => {
4
+ try {
5
+ new PublicKey(address);
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ };
@@ -1,4 +0,0 @@
1
- import type { Connector } from 'wagmi';
2
- export declare const UTXODisconnectIconButton: ({ connector, }: {
3
- connector?: Connector;
4
- }) => import("react/jsx-runtime").JSX.Element;
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useConfig } from '@lifi/wallet-management';
3
- import { PowerSettingsNewRounded } from '@mui/icons-material';
4
- import { IconButton } from '@mui/material';
5
- import { disconnect } from 'wagmi/actions';
6
- export const UTXODisconnectIconButton = ({ connector, }) => {
7
- const config = useConfig();
8
- return (_jsx(IconButton, { size: "medium", onClick: async (e) => {
9
- e.stopPropagation();
10
- await disconnect(config, { connector });
11
- }, children: _jsx(PowerSettingsNewRounded, {}) }));
12
- };
13
- //# sourceMappingURL=UTXODisconnectIconButton.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UTXODisconnectIconButton.js","sourceRoot":"","sources":["../../../components/Header/UTXODisconnectIconButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,EACvC,SAAS,GAGV,EAAE,EAAE;IACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,CACL,KAAC,UAAU,IACT,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YACnB,CAAC,CAAC,eAAe,EAAE,CAAC;YACpB,MAAM,UAAU,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC1C,CAAC,YAED,KAAC,uBAAuB,KAAG,GAChB,CACd,CAAC;AACJ,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- import type { CreateConnectorFnExtended } from '@lifi/wallet-management';
2
- import type { Connector } from 'wagmi';
3
- interface UTXOListItemButtonProps {
4
- connector: CreateConnectorFnExtended | Connector;
5
- onNotInstalled(connector: Connector): void;
6
- }
7
- export declare const UTXOListItemButton: ({ connector, onNotInstalled, }: UTXOListItemButtonProps) => import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,39 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChainType } from '@lifi/sdk';
3
- import { getConnectorIcon, isWalletInstalledAsync, useConfig, } from '@lifi/wallet-management';
4
- import { Avatar, ListItemAvatar } from '@mui/material';
5
- import { connect, disconnect, getAccount } from 'wagmi/actions';
6
- import { ListItemButton } from '../../components/ListItemButton.js';
7
- import { ListItemText } from '../../components/ListItemText.js';
8
- import { useLastConnectedAccount } from '../../hooks/useAccount.js';
9
- import { useNavigateBack } from '../../hooks/useNavigateBack.js';
10
- import { useWidgetEvents } from '../../hooks/useWidgetEvents.js';
11
- import { WidgetEvent } from '../../types/events.js';
12
- export const UTXOListItemButton = ({ connector, onNotInstalled, }) => {
13
- const { navigateBack } = useNavigateBack();
14
- const emitter = useWidgetEvents();
15
- const config = useConfig();
16
- const { setLastConnectedAccount } = useLastConnectedAccount();
17
- const handleUTXOConnect = async () => {
18
- const identityCheckPassed = await isWalletInstalledAsync(connector.id);
19
- if (!identityCheckPassed) {
20
- onNotInstalled(connector);
21
- return;
22
- }
23
- const connectedAccount = getAccount(config);
24
- if (connectedAccount.connector) {
25
- await disconnect(config, { connector: connectedAccount.connector });
26
- }
27
- const data = await connect(config, { connector });
28
- setLastConnectedAccount(connector);
29
- emitter.emit(WidgetEvent.WalletConnected, {
30
- address: data.accounts[0],
31
- chainId: data.chainId,
32
- chainType: ChainType.UTXO,
33
- });
34
- navigateBack();
35
- };
36
- const connectorName = connector.displayName || connector.name;
37
- return (_jsxs(ListItemButton, { onClick: handleUTXOConnect, children: [_jsx(ListItemAvatar, { children: _jsx(Avatar, { src: getConnectorIcon(connector), alt: connectorName, children: connectorName?.[0] }) }), _jsx(ListItemText, { primary: `${connectorName} (Bitcoin)` })] }, connector.id));
38
- };
39
- //# sourceMappingURL=UTXOListItemButton.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UTXOListItemButton.js","sourceRoot":"","sources":["../../../pages/SelectWalletPage/UTXOListItemButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,SAAS,GACV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAOpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EACjC,SAAS,EACT,cAAc,GACU,EAAE,EAAE;IAC5B,MAAM,EAAE,YAAY,EAAE,GAAG,eAAe,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,EAAE,uBAAuB,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAE9D,MAAM,iBAAiB,GAAG,KAAK,IAAI,EAAE;QACnC,MAAM,mBAAmB,GAAG,MAAM,sBAAsB,CACrD,SAAuB,CAAC,EAAE,CAC5B,CAAC;QACF,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACzB,cAAc,CAAC,SAAsB,CAAC,CAAC;YACvC,OAAO;QACT,CAAC;QACD,MAAM,gBAAgB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,gBAAgB,CAAC,SAAS,EAAE,CAAC;YAC/B,MAAM,UAAU,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAClD,uBAAuB,CAAC,SAAS,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE;YACxC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,SAAS,CAAC,IAAI;SAC1B,CAAC,CAAC;QACH,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,aAAa,GAChB,SAAuC,CAAC,WAAW,IAAI,SAAS,CAAC,IAAI,CAAC;IAEzE,OAAO,CACL,MAAC,cAAc,IAAoB,OAAO,EAAE,iBAAiB,aAC3D,KAAC,cAAc,cACb,KAAC,MAAM,IACL,GAAG,EAAE,gBAAgB,CAAC,SAAsB,CAAC,EAC7C,GAAG,EAAE,aAAa,YAEjB,aAAa,EAAE,CAAC,CAAC,CAAC,GACZ,GACM,EACjB,KAAC,YAAY,IAAC,OAAO,EAAE,GAAG,aAAa,YAAY,GAAI,KATpC,SAAS,CAAC,EAAE,CAUhB,CAClB,CAAC;AACJ,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- import { type FC, type PropsWithChildren } from 'react';
2
- export declare const UTXOBaseProvider: FC<PropsWithChildren>;
@@ -1,17 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { BigmiProvider, createDefaultBigmiConfig, useReconnect, } from '@lifi/wallet-management';
3
- import { useRef } from 'react';
4
- export const UTXOBaseProvider = ({ children }) => {
5
- const bigmi = useRef();
6
- if (!bigmi.current) {
7
- bigmi.current = createDefaultBigmiConfig({
8
- bigmiConfig: {
9
- ssr: true,
10
- multiInjectedProviderDiscovery: false,
11
- },
12
- });
13
- }
14
- useReconnect(bigmi.current.config);
15
- return (_jsx(BigmiProvider, { config: bigmi.current.config, reconnectOnMount: false, children: children }));
16
- };
17
- //# sourceMappingURL=UTXOBaseProvider.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UTXOBaseProvider.js","sourceRoot":"","sources":["../../../providers/WalletProvider/UTXOBaseProvider.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,YAAY,GACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAmC,MAAM,OAAO,CAAC;AAEhE,MAAM,CAAC,MAAM,gBAAgB,GAA0B,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;IACtE,MAAM,KAAK,GAAG,MAAM,EAA4B,CAAC;IAEjD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,KAAK,CAAC,OAAO,GAAG,wBAAwB,CAAC;YACvC,WAAW,EAAE;gBACX,GAAG,EAAE,IAAI;gBACT,8BAA8B,EAAE,KAAK;aACtC;SACF,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,CACL,KAAC,aAAa,IAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,YACjE,QAAQ,GACK,CACjB,CAAC;AACJ,CAAC,CAAC"}