@lifi/widget 3.6.0-alpha.5 → 3.6.1

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 (193) hide show
  1. package/App.tsx +3 -3
  2. package/AppProvider.tsx +2 -1
  3. package/CHANGELOG.md +23 -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/format.js +1 -1
  119. package/_esm/utils/format.js.map +1 -1
  120. package/_esm/utils/svm.d.ts +1 -0
  121. package/_esm/utils/svm.js +11 -0
  122. package/_esm/utils/svm.js.map +1 -0
  123. package/components/ChainSelect/useChainSelect.ts +1 -10
  124. package/components/FeeBreakdownTooltip.tsx +1 -4
  125. package/components/Header/EVMDisconnectIconButton.tsx +5 -5
  126. package/components/Header/Header.style.ts +0 -6
  127. package/components/Header/WalletMenu.style.tsx +1 -5
  128. package/components/Header/WalletMenu.tsx +5 -6
  129. package/components/SendToWallet/SendToWalletButton.tsx +23 -13
  130. package/components/SendToWallet/SendToWalletExpandButton.tsx +10 -10
  131. package/components/Step/StepProcess.tsx +12 -2
  132. package/components/TokenList/TokenListItem.tsx +7 -15
  133. package/components/TokenList/VirtualizedTokenList.tsx +1 -1
  134. package/components/TokenList/types.ts +2 -2
  135. package/components/TokenRate/TokenRate.tsx +2 -2
  136. package/components/TransactionDetails.tsx +4 -1
  137. package/config/version.ts +1 -1
  138. package/hooks/useAccount.ts +35 -82
  139. package/hooks/useAvailableChains.ts +10 -9
  140. package/hooks/useChains.ts +3 -6
  141. package/hooks/useExplorer.ts +19 -2
  142. package/hooks/useGasSufficiency.ts +2 -4
  143. package/hooks/useHeaderHeight.ts +8 -1
  144. package/hooks/useTokens.ts +1 -2
  145. package/hooks/useWallets.ts +20 -41
  146. package/i18n/en.json +1 -1
  147. package/package.json +8 -8
  148. package/pages/SelectWalletPage/EVMListItemButton.tsx +19 -15
  149. package/pages/SelectWalletPage/SVMListItemButton.tsx +0 -3
  150. package/pages/SelectWalletPage/SelectWalletPage.tsx +4 -9
  151. package/pages/TransactionDetailsPage/TransferIdCard.tsx +1 -1
  152. package/pages/TransactionPage/StatusBottomSheet.tsx +5 -0
  153. package/providers/I18nProvider/types.ts +2 -2
  154. package/providers/WalletProvider/SDKProviders.tsx +7 -25
  155. package/providers/WalletProvider/WalletProvider.tsx +2 -5
  156. package/providers/WalletProvider/useHasExternalWalletProvider.ts +5 -11
  157. package/providers/WidgetProvider/WidgetProvider.tsx +1 -39
  158. package/stores/StoreProvider.tsx +5 -4
  159. package/stores/form/FormStore.tsx +100 -20
  160. package/stores/form/FormUpdater.tsx +52 -22
  161. package/stores/form/URLSearchParamsBuilder.tsx +55 -5
  162. package/stores/form/createFormStore.ts +9 -0
  163. package/stores/form/types.ts +14 -10
  164. package/stores/form/useFieldActions.ts +1 -0
  165. package/stores/form/useFormRef.ts +69 -0
  166. package/stores/settings/types.ts +0 -2
  167. package/stores/settings/useSendToWalletStore.ts +0 -8
  168. package/themes/createTheme.ts +1 -3
  169. package/types/widget.ts +46 -3
  170. package/utils/chainType.ts +3 -2
  171. package/utils/fees.ts +13 -15
  172. package/utils/format.ts +1 -1
  173. package/utils/svm.ts +10 -0
  174. package/_esm/components/Header/UTXODisconnectIconButton.d.ts +0 -4
  175. package/_esm/components/Header/UTXODisconnectIconButton.js +0 -13
  176. package/_esm/components/Header/UTXODisconnectIconButton.js.map +0 -1
  177. package/_esm/pages/SelectWalletPage/UTXOListItemButton.d.ts +0 -8
  178. package/_esm/pages/SelectWalletPage/UTXOListItemButton.js +0 -39
  179. package/_esm/pages/SelectWalletPage/UTXOListItemButton.js.map +0 -1
  180. package/_esm/providers/WalletProvider/UTXOBaseProvider.d.ts +0 -2
  181. package/_esm/providers/WalletProvider/UTXOBaseProvider.js +0 -17
  182. package/_esm/providers/WalletProvider/UTXOBaseProvider.js.map +0 -1
  183. package/_esm/providers/WalletProvider/UTXOExternalContext.d.ts +0 -1
  184. package/_esm/providers/WalletProvider/UTXOExternalContext.js +0 -3
  185. package/_esm/providers/WalletProvider/UTXOExternalContext.js.map +0 -1
  186. package/_esm/providers/WalletProvider/UTXOProvider.d.ts +0 -3
  187. package/_esm/providers/WalletProvider/UTXOProvider.js +0 -18
  188. package/_esm/providers/WalletProvider/UTXOProvider.js.map +0 -1
  189. package/components/Header/UTXODisconnectIconButton.tsx +0 -24
  190. package/pages/SelectWalletPage/UTXOListItemButton.tsx +0 -70
  191. package/providers/WalletProvider/UTXOBaseProvider.tsx +0 -28
  192. package/providers/WalletProvider/UTXOExternalContext.ts +0 -3
  193. package/providers/WalletProvider/UTXOProvider.tsx +0 -26
@@ -1,63 +1,143 @@
1
1
  import type { PropsWithChildren } from 'react';
2
2
  import { useMemo, useRef } from 'react';
3
3
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js';
4
+ import type { FormRef, ToAddress } from '../../types/widget.js';
4
5
  import { HiddenUI } from '../../types/widget.js';
5
6
  import { FormStoreContext } from './FormStoreContext.js';
6
7
  import { FormUpdater } from './FormUpdater.js';
7
8
  import { createFormStore, formDefaultValues } from './createFormStore.js';
8
- import type { FormStoreStore } from './types.js';
9
+ import type { DefaultValues, FormStoreStore } from './types.js';
10
+ import { useFormRef } from './useFormRef.js';
9
11
 
10
- export const FormStoreProvider: React.FC<PropsWithChildren> = ({
12
+ // decorates and initialise the form date for use in the form store
13
+ const initialiseDefaultValues = (
14
+ defaultValues: Partial<DefaultValues>,
15
+ fromAmount?: number | string,
16
+ toAmount?: number | string,
17
+ toAddress?: ToAddress,
18
+ hiddenToAddress?: boolean,
19
+ ) => ({
20
+ ...formDefaultValues,
21
+ ...defaultValues,
22
+ fromAmount:
23
+ (typeof fromAmount === 'number' ? fromAmount?.toPrecision() : fromAmount) ||
24
+ formDefaultValues.fromAmount,
25
+ toAmount:
26
+ (typeof toAmount === 'number' ? toAmount?.toPrecision() : toAmount) ||
27
+ formDefaultValues.toAmount,
28
+ // Prevent setting address when the field is hidden
29
+ toAddress: hiddenToAddress
30
+ ? formDefaultValues.toAddress
31
+ : toAddress?.address || formDefaultValues.toAddress,
32
+ });
33
+
34
+ interface FormStoreProviderProps extends PropsWithChildren {
35
+ formRef?: FormRef;
36
+ }
37
+
38
+ export const FormStoreProvider: React.FC<FormStoreProviderProps> = ({
11
39
  children,
40
+ formRef,
12
41
  }) => {
42
+ const widgetConfig = useWidgetConfig();
43
+
13
44
  const {
14
45
  fromChain,
15
46
  fromToken,
16
- fromAmount,
17
47
  toChain,
18
48
  toToken,
49
+ fromAmount,
50
+ toAmount,
19
51
  toAddress,
20
52
  hiddenUI,
21
- } = useWidgetConfig();
53
+ formUpdateKey,
54
+ } = widgetConfig;
55
+
22
56
  const storeRef = useRef<FormStoreStore>();
23
57
 
24
58
  const hiddenToAddress = hiddenUI?.includes(HiddenUI.ToAddress);
25
59
 
26
- const defaultValues = useMemo(
60
+ const configHasFromChain = widgetConfig.hasOwnProperty('fromChain');
61
+ const configHasFromToken = widgetConfig.hasOwnProperty('fromToken');
62
+ const configHasFromAmount = widgetConfig.hasOwnProperty('fromAmount');
63
+ const configHasToAmount = widgetConfig.hasOwnProperty('toAmount');
64
+ const configHasToAddress = widgetConfig.hasOwnProperty('toAddress');
65
+ const configHasToChain = widgetConfig.hasOwnProperty('toChain');
66
+ const configHasToToken = widgetConfig.hasOwnProperty('toToken');
67
+
68
+ // We use the presence/absence of a property to decide if the form values in state need to be updated
69
+ // We only build and set a property on the memoized form values here if they are included in the
70
+ // config - undefined is considered a valid value that will reset that form field
71
+ const reactiveFormValues = useMemo(
27
72
  () => ({
28
- ...formDefaultValues,
29
- fromChain,
30
- fromToken,
31
- toChain,
32
- toToken,
33
- fromAmount:
34
- (typeof fromAmount === 'number'
35
- ? fromAmount?.toPrecision()
36
- : fromAmount) || formDefaultValues.fromAmount,
37
- // Prevent setting address when the field is hidden
38
- toAddress: hiddenToAddress
39
- ? formDefaultValues.toAddress
40
- : toAddress?.address || formDefaultValues.toAddress,
73
+ ...(configHasFromChain ? { fromChain } : undefined),
74
+ ...(configHasFromToken ? { fromToken } : undefined),
75
+ ...(configHasFromAmount
76
+ ? {
77
+ fromAmount:
78
+ (typeof fromAmount === 'number'
79
+ ? fromAmount?.toPrecision()
80
+ : fromAmount) || formDefaultValues.fromAmount,
81
+ }
82
+ : undefined),
83
+ ...(configHasFromAmount
84
+ ? {
85
+ toAmount:
86
+ (typeof toAmount === 'number'
87
+ ? toAmount?.toPrecision()
88
+ : toAmount) || formDefaultValues.toAmount,
89
+ }
90
+ : undefined),
91
+ ...(configHasToChain ? { toChain } : undefined),
92
+ ...(configHasToToken ? { toToken } : undefined),
93
+ ...(configHasToAddress
94
+ ? {
95
+ toAddress: hiddenToAddress
96
+ ? formDefaultValues.toAddress
97
+ : toAddress?.address || formDefaultValues.toAddress,
98
+ }
99
+ : undefined),
41
100
  }),
101
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
102
  [
43
103
  fromAmount,
104
+ toAmount,
44
105
  fromChain,
45
106
  fromToken,
46
107
  hiddenToAddress,
47
108
  toAddress,
48
109
  toChain,
49
110
  toToken,
111
+ // formUpdateKey should be a randomly assigned unique key. It can be used to force updates for the form field values
112
+ formUpdateKey,
113
+ configHasFromChain,
114
+ configHasFromToken,
115
+ configHasFromAmount,
116
+ configHasToAmount,
117
+ configHasToAddress,
118
+ configHasToChain,
119
+ configHasToToken,
50
120
  ],
51
121
  );
52
122
 
53
123
  if (!storeRef.current) {
54
- storeRef.current = createFormStore(defaultValues);
124
+ storeRef.current = createFormStore(
125
+ initialiseDefaultValues(
126
+ reactiveFormValues,
127
+ fromAmount,
128
+ toAmount,
129
+ toAddress,
130
+ hiddenToAddress,
131
+ ),
132
+ );
55
133
  }
56
134
 
135
+ useFormRef(storeRef.current, formRef);
136
+
57
137
  return (
58
138
  <FormStoreContext.Provider value={storeRef.current}>
59
139
  {children}
60
- <FormUpdater defaultValues={defaultValues} />
140
+ <FormUpdater reactiveFormValues={reactiveFormValues} />
61
141
  </FormStoreContext.Provider>
62
142
  );
63
143
  };
@@ -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 = (