@lifi/widget 3.8.2 → 3.10.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 (132) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/esm/components/ChainSelect/useChainSelect.js +9 -3
  3. package/dist/esm/components/ChainSelect/useChainSelect.js.map +1 -1
  4. package/dist/esm/components/Header/CloseDrawerButton.js +4 -3
  5. package/dist/esm/components/Header/CloseDrawerButton.js.map +1 -1
  6. package/dist/esm/components/Header/WalletHeader.js +23 -13
  7. package/dist/esm/components/Header/WalletHeader.js.map +1 -1
  8. package/dist/esm/components/ReverseTokensButton/ReverseTokensButton.js +16 -1
  9. package/dist/esm/components/ReverseTokensButton/ReverseTokensButton.js.map +1 -1
  10. package/dist/esm/components/Step/StepTimer.js +13 -6
  11. package/dist/esm/components/Step/StepTimer.js.map +1 -1
  12. package/dist/esm/components/TokenList/TokenList.js +6 -4
  13. package/dist/esm/components/TokenList/TokenList.js.map +1 -1
  14. package/dist/esm/components/TokenList/TokenListItem.js +1 -1
  15. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  16. package/dist/esm/components/TokenList/types.d.ts +2 -2
  17. package/dist/esm/components/TokenList/useTokenSelect.d.ts +4 -0
  18. package/dist/esm/components/TokenList/useTokenSelect.js +19 -10
  19. package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
  20. package/dist/esm/config/version.d.ts +1 -1
  21. package/dist/esm/config/version.js +1 -1
  22. package/dist/esm/config/version.js.map +1 -1
  23. package/dist/esm/hooks/useAvailableChains.d.ts +2 -1
  24. package/dist/esm/hooks/useAvailableChains.js.map +1 -1
  25. package/dist/esm/hooks/useChain.d.ts +1 -0
  26. package/dist/esm/hooks/useChain.js +1 -0
  27. package/dist/esm/hooks/useChain.js.map +1 -1
  28. package/dist/esm/hooks/useChains.d.ts +1 -1
  29. package/dist/esm/hooks/useGasSufficiency.d.ts +1 -1
  30. package/dist/esm/hooks/useGasSufficiency.js +2 -12
  31. package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
  32. package/dist/esm/hooks/useIsContractAddress.d.ts +2 -0
  33. package/dist/esm/hooks/useIsContractAddress.js +16 -0
  34. package/dist/esm/hooks/useIsContractAddress.js.map +1 -0
  35. package/dist/esm/hooks/useToAddressAutoPopulate.d.ts +12 -0
  36. package/dist/esm/hooks/useToAddressAutoPopulate.js +68 -0
  37. package/dist/esm/hooks/useToAddressAutoPopulate.js.map +1 -0
  38. package/dist/esm/hooks/useToAddressRequirements.d.ts +1 -1
  39. package/dist/esm/hooks/useToAddressRequirements.js +9 -1
  40. package/dist/esm/hooks/useToAddressRequirements.js.map +1 -1
  41. package/dist/esm/hooks/useToAddressReset.js +15 -8
  42. package/dist/esm/hooks/useToAddressReset.js.map +1 -1
  43. package/dist/esm/i18n/bn.json +3 -4
  44. package/dist/esm/i18n/de.json +3 -4
  45. package/dist/esm/i18n/es.json +3 -4
  46. package/dist/esm/i18n/fr.json +3 -4
  47. package/dist/esm/i18n/hi.json +3 -4
  48. package/dist/esm/i18n/id.json +3 -4
  49. package/dist/esm/i18n/it.json +3 -4
  50. package/dist/esm/i18n/ja.json +3 -4
  51. package/dist/esm/i18n/ko.json +3 -4
  52. package/dist/esm/i18n/pt.json +3 -4
  53. package/dist/esm/i18n/th.json +3 -4
  54. package/dist/esm/i18n/tr.json +3 -4
  55. package/dist/esm/i18n/uk.json +3 -4
  56. package/dist/esm/i18n/vi.json +3 -4
  57. package/dist/esm/i18n/zh.json +3 -4
  58. package/dist/esm/pages/SendToWallet/BookmarksPage.js +1 -0
  59. package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
  60. package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js +1 -0
  61. package/dist/esm/pages/SendToWallet/ConfirmAddressSheet.js.map +1 -1
  62. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +3 -2
  63. package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
  64. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +3 -2
  65. package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
  66. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +4 -1
  67. package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
  68. package/dist/esm/providers/WalletProvider/WalletProvider.js +8 -2
  69. package/dist/esm/providers/WalletProvider/WalletProvider.js.map +1 -1
  70. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.d.ts +8 -0
  71. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js +39 -0
  72. package/dist/esm/providers/WalletProvider/useExternalWalletProvider.js.map +1 -0
  73. package/dist/esm/stores/chains/ChainOrderStore.js +26 -5
  74. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  75. package/dist/esm/stores/form/createFormStore.js +1 -0
  76. package/dist/esm/stores/form/createFormStore.js.map +1 -1
  77. package/dist/esm/stores/form/types.d.ts +1 -0
  78. package/dist/esm/stores/form/types.js.map +1 -1
  79. package/dist/esm/stores/form/useFieldActions.d.ts +1 -0
  80. package/dist/esm/stores/form/useFieldActions.js +1 -0
  81. package/dist/esm/stores/form/useFieldActions.js.map +1 -1
  82. package/dist/esm/types/widget.d.ts +9 -0
  83. package/package.json +8 -8
  84. package/src/components/ChainSelect/useChainSelect.ts +9 -4
  85. package/src/components/Header/CloseDrawerButton.tsx +4 -3
  86. package/src/components/Header/WalletHeader.tsx +34 -17
  87. package/src/components/ReverseTokensButton/ReverseTokensButton.tsx +25 -6
  88. package/src/components/Step/StepTimer.tsx +29 -19
  89. package/src/components/TokenList/TokenList.tsx +7 -4
  90. package/src/components/TokenList/TokenListItem.tsx +1 -1
  91. package/src/components/TokenList/types.ts +2 -2
  92. package/src/components/TokenList/useTokenSelect.ts +39 -16
  93. package/src/config/version.ts +1 -1
  94. package/src/hooks/useAvailableChains.ts +6 -1
  95. package/src/hooks/useChain.ts +1 -0
  96. package/src/hooks/useGasSufficiency.ts +7 -19
  97. package/src/hooks/useIsContractAddress.ts +21 -0
  98. package/src/hooks/useToAddressAutoPopulate.ts +95 -0
  99. package/src/hooks/useToAddressRequirements.ts +13 -2
  100. package/src/hooks/useToAddressReset.ts +15 -8
  101. package/src/i18n/bn.json +3 -4
  102. package/src/i18n/de.json +3 -4
  103. package/src/i18n/es.json +3 -4
  104. package/src/i18n/fr.json +3 -4
  105. package/src/i18n/hi.json +3 -4
  106. package/src/i18n/id.json +3 -4
  107. package/src/i18n/it.json +3 -4
  108. package/src/i18n/ja.json +3 -4
  109. package/src/i18n/ko.json +3 -4
  110. package/src/i18n/pt.json +3 -4
  111. package/src/i18n/th.json +3 -4
  112. package/src/i18n/tr.json +3 -4
  113. package/src/i18n/uk.json +3 -4
  114. package/src/i18n/vi.json +3 -4
  115. package/src/i18n/zh.json +3 -4
  116. package/src/pages/SendToWallet/BookmarksPage.tsx +1 -0
  117. package/src/pages/SendToWallet/ConfirmAddressSheet.tsx +1 -0
  118. package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +3 -2
  119. package/src/pages/SendToWallet/RecentWalletsPage.tsx +3 -2
  120. package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +4 -1
  121. package/src/providers/WalletProvider/WalletProvider.tsx +9 -2
  122. package/src/providers/WalletProvider/useExternalWalletProvider.ts +53 -0
  123. package/src/stores/chains/ChainOrderStore.tsx +28 -5
  124. package/src/stores/form/createFormStore.ts +2 -0
  125. package/src/stores/form/types.ts +1 -0
  126. package/src/stores/form/useFieldActions.ts +1 -0
  127. package/src/stores/header/useHeaderStore.tsx +1 -1
  128. package/src/types/widget.ts +9 -0
  129. package/dist/esm/providers/WalletProvider/useHasExternalWalletProvider.d.ts +0 -7
  130. package/dist/esm/providers/WalletProvider/useHasExternalWalletProvider.js +0 -28
  131. package/dist/esm/providers/WalletProvider/useHasExternalWalletProvider.js.map +0 -1
  132. package/src/providers/WalletProvider/useHasExternalWalletProvider.ts +0 -36
@@ -4,6 +4,7 @@ export declare const useFieldActions: () => {
4
4
  setUserAndDefaultValues: (formValues: Partial<DefaultValues>) => void;
5
5
  setDefaultValues: (formValues: DefaultValues) => void;
6
6
  isTouched: (fieldName: FormFieldNames) => boolean;
7
+ isDirty: (fieldName: FormFieldNames) => boolean;
7
8
  setAsTouched: (fieldName: FormFieldNames) => void;
8
9
  resetField: (fieldName: FormFieldNames, resetOptions?: import("./types.js").ResetOptions) => void;
9
10
  getFieldValues: <T extends FormFieldNames[]>(...names: T) => import("./types.js").FormFieldArray<T>;
@@ -8,6 +8,7 @@ export const useFieldActions = () => {
8
8
  const actions = useFormStore((store) => ({
9
9
  getFieldValues: store.getFieldValues,
10
10
  isTouched: store.isTouched,
11
+ isDirty: store.isDirty,
11
12
  resetField: store.resetField,
12
13
  setAsTouched: store.setAsTouched,
13
14
  setDefaultValues: store.setDefaultValues,
@@ -1 +1 @@
1
- {"version":3,"file":"useFieldActions.js","sourceRoot":"","sources":["../../../../src/stores/form/useFieldActions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAEhE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAQnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,EAAE;IAClC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;IACjC,MAAM,OAAO,GAAG,YAAY,CAC1B,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACV,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,uBAAuB,EAAE,KAAK,CAAC,uBAAuB;KACvD,CAAC,EACF,OAAO,CACR,CAAA;IAED,MAAM,8BAA8B,GAAG,WAAW,CAChD,CACE,SAAyB,EACzB,QAA0B,EAC1B,OAAoB,EACpB,EAAE;QACF,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;QAErD,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAEnD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE;gBACzC,SAAS;gBACT,QAAQ;gBACR,QAAQ;aACW,CAAC,CAAA;QACxB,CAAC;IACH,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAA;IAED,MAAM,wCAAwC,GAAG,WAAW,CAC1D,CAAC,UAAkC,EAAE,EAAE;QACrC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAqB,CAAA;QAElE,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CACzC,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;YACnB,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;YACrD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;YAEtC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC/C,CAAC;YAED,OAAO,KAAK,CAAA;QACd,CAAC,EACD,EAIG,CACJ,CAAA;QAED,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAA;QAE3C,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC1D,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE;gBACzC,SAAS;gBACT,QAAQ;gBACR,QAAQ;aACW,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;IACJ,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAA;IAED,OAAO;QACL,GAAG,OAAO;QACV,aAAa,EAAE,8BAA8B;QAC7C,uBAAuB,EAAE,wCAAwC;KAClE,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"useFieldActions.js","sourceRoot":"","sources":["../../../../src/stores/form/useFieldActions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAEhE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAQnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,EAAE;IAClC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;IACjC,MAAM,OAAO,GAAG,YAAY,CAC1B,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACV,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,uBAAuB,EAAE,KAAK,CAAC,uBAAuB;KACvD,CAAC,EACF,OAAO,CACR,CAAA;IAED,MAAM,8BAA8B,GAAG,WAAW,CAChD,CACE,SAAyB,EACzB,QAA0B,EAC1B,OAAoB,EACpB,EAAE;QACF,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;QAErD,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAEnD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE;gBACzC,SAAS;gBACT,QAAQ;gBACR,QAAQ;aACW,CAAC,CAAA;QACxB,CAAC;IACH,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAA;IAED,MAAM,wCAAwC,GAAG,WAAW,CAC1D,CAAC,UAAkC,EAAE,EAAE;QACrC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAqB,CAAA;QAElE,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CACzC,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;YACnB,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;YACrD,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;YAEtC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC/C,CAAC;YAED,OAAO,KAAK,CAAA;QACd,CAAC,EACD,EAIG,CACJ,CAAA;QAED,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAA;QAE3C,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC1D,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE;gBACzC,SAAS;gBACT,QAAQ;gBACR,QAAQ;aACW,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;IACJ,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAA;IAED,OAAO;QACL,GAAG,OAAO;QACV,aAAa,EAAE,8BAA8B;QAC7C,uBAAuB,EAAE,wCAAwC;KAClE,CAAA;AACH,CAAC,CAAA"}
@@ -60,6 +60,15 @@ export interface WidgetWalletConfig {
60
60
  walletConnect?: WalletConnectParameters;
61
61
  coinbase?: CoinbaseWalletParameters;
62
62
  metaMask?: MetaMaskParameters;
63
+ /**
64
+ * Determines whether the widget should provide partial wallet management functionality.
65
+ *
66
+ * In partial mode, external wallet management will be used for "opt-out" providers,
67
+ * while the internal management is applied for any remaining providers that do not opt out.
68
+ * This allows a flexible balance between the integrator’s custom wallet menu and the widget’s native wallet menu.
69
+ * @default false
70
+ */
71
+ usePartialWalletManagement?: boolean;
63
72
  }
64
73
  export interface WidgetSDKConfig extends Omit<SDKConfig, 'apiKey' | 'disableVersionCheck' | 'integrator' | 'routeOptions' | 'widgetVersion'> {
65
74
  routeOptions?: Omit<RouteOptions, 'bridges' | 'exchanges'>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/widget",
3
- "version": "3.8.2",
3
+ "version": "3.10.0",
4
4
  "description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -30,12 +30,12 @@
30
30
  "lifi"
31
31
  ],
32
32
  "dependencies": {
33
- "@bigmi/client": "^0.0.3",
34
- "@bigmi/core": "^0.0.3",
35
- "@bigmi/react": "^0.0.3",
33
+ "@bigmi/client": "^0.0.4",
34
+ "@bigmi/core": "^0.0.4",
35
+ "@bigmi/react": "^0.0.4",
36
36
  "@emotion/react": "^11.13.3",
37
37
  "@emotion/styled": "^11.13.0",
38
- "@lifi/sdk": "^3.3.1",
38
+ "@lifi/sdk": "^3.4.0",
39
39
  "@mui/icons-material": "^5.16.7",
40
40
  "@mui/lab": "^5.0.0-alpha.173",
41
41
  "@mui/material": "^5.16.7",
@@ -45,7 +45,7 @@
45
45
  "@solana/web3.js": "^1.95.4",
46
46
  "@tanstack/react-query": "^5.59.16",
47
47
  "@tanstack/react-virtual": "^3.10.8",
48
- "i18next": "^23.16.3",
48
+ "i18next": "^23.16.4",
49
49
  "microdiff": "^1.4.0",
50
50
  "mitt": "^3.0.1",
51
51
  "react": "^18.3.1",
@@ -53,10 +53,10 @@
53
53
  "react-i18next": "^15.1.0",
54
54
  "react-intersection-observer": "^9.13.1",
55
55
  "react-router-dom": "^6.27.0",
56
- "viem": "^2.21.34",
56
+ "viem": "^2.21.35",
57
57
  "wagmi": "^2.12.25",
58
58
  "zustand": "^4.5.5",
59
- "@lifi/wallet-management": "^3.3.1"
59
+ "@lifi/wallet-management": "^3.4.1"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "react": ">=18",
@@ -2,7 +2,7 @@ import type { EVMChain } from '@lifi/sdk'
2
2
  import { useChains } from '../../hooks/useChains.js'
3
3
  import { useSwapOnly } from '../../hooks/useSwapOnly.js'
4
4
  import { useToAddressReset } from '../../hooks/useToAddressReset.js'
5
- import { useHasExternalWalletProvider } from '../../providers/WalletProvider/useHasExternalWalletProvider.js'
5
+ import { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'
6
6
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
7
7
  import { useChainOrder } from '../../stores/chains/useChainOrder.js'
8
8
  import type { FormType } from '../../stores/form/types.js'
@@ -16,11 +16,16 @@ export const useChainSelect = (formType: FormType) => {
16
16
  const chainKey = FormKeyHelper.getChainKey(formType)
17
17
  const { onChange } = useFieldController({ name: chainKey })
18
18
  const { setFieldValue, getFieldValues } = useFieldActions()
19
- const { hasExternalProvider, availableChainTypes } =
20
- useHasExternalWalletProvider()
19
+ const { useExternalWalletProvidersOnly, externalChainTypes } =
20
+ useExternalWalletProvider()
21
21
  const { chains, isLoading, getChainById } = useChains(
22
22
  formType,
23
- formType === 'from' && hasExternalProvider ? availableChainTypes : undefined
23
+ // If the integrator uses external wallet management and has not opted in for partial wallet management,
24
+ // restrict the displayed chains to those compatible with external wallet management.
25
+ // This ensures users only see chains for which they can sign transactions.
26
+ formType === 'from' && useExternalWalletProvidersOnly
27
+ ? externalChainTypes
28
+ : undefined
24
29
  )
25
30
 
26
31
  const [chainOrder, setChainOrder] = useChainOrder(formType)
@@ -2,7 +2,7 @@ import { CloseRounded } from '@mui/icons-material'
2
2
  import { IconButton, Tooltip } from '@mui/material'
3
3
  import { useTranslation } from 'react-i18next'
4
4
  import { useDrawer } from '../../AppDrawerContext.js'
5
- import { useHasExternalWalletProvider } from '../../providers/WalletProvider/useHasExternalWalletProvider.js'
5
+ import { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'
6
6
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
7
7
 
8
8
  interface CloseDrawerButtonProps {
@@ -13,10 +13,11 @@ export const CloseDrawerButton = ({ header }: CloseDrawerButtonProps) => {
13
13
  const { t } = useTranslation()
14
14
  const { subvariant } = useWidgetConfig()
15
15
  const { closeDrawer } = useDrawer()
16
- const { hasExternalProvider } = useHasExternalWalletProvider()
16
+ const { useExternalWalletProvidersOnly } = useExternalWalletProvider()
17
17
 
18
18
  const showInNavigationHeader =
19
- header === 'navigation' && (hasExternalProvider || subvariant === 'split')
19
+ header === 'navigation' &&
20
+ (useExternalWalletProvidersOnly || subvariant === 'split')
20
21
 
21
22
  const showInWalletHeader = header === 'wallet' && subvariant !== 'split'
22
23
 
@@ -9,8 +9,9 @@ import { Avatar, Badge } from '@mui/material'
9
9
  import { useState } from 'react'
10
10
  import { useTranslation } from 'react-i18next'
11
11
  import { useChain } from '../../hooks/useChain.js'
12
- import { useHasExternalWalletProvider } from '../../providers/WalletProvider/useHasExternalWalletProvider.js'
12
+ import { useExternalWalletProvider } from '../../providers/WalletProvider/useExternalWalletProvider.js'
13
13
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
14
+ import { useFieldValues } from '../../stores/form/useFieldValues.js'
14
15
  import { HiddenUI } from '../../types/widget.js'
15
16
  import { shortenAddress } from '../../utils/wallet.js'
16
17
  import { SmallAvatar } from '../Avatar/SmallAvatar.js'
@@ -24,12 +25,23 @@ import {
24
25
  import { WalletMenu } from './WalletMenu.js'
25
26
  import { WalletMenuContainer } from './WalletMenu.style.js'
26
27
 
27
- export const WalletHeader: React.FC = () => {
28
+ const useInternalWalletManagement = () => {
28
29
  const { subvariant, hiddenUI } = useWidgetConfig()
29
- const { hasExternalProvider } = useHasExternalWalletProvider()
30
- return !hasExternalProvider &&
31
- subvariant !== 'split' &&
32
- !hiddenUI?.includes(HiddenUI.WalletMenu) ? (
30
+ const { useExternalWalletProvidersOnly } = useExternalWalletProvider()
31
+
32
+ const isSplitVariant = subvariant === 'split'
33
+ const isWalletMenuHidden = hiddenUI?.includes(HiddenUI.WalletMenu)
34
+
35
+ const shouldShowWalletMenu =
36
+ !useExternalWalletProvidersOnly && !isSplitVariant && !isWalletMenuHidden
37
+
38
+ return shouldShowWalletMenu
39
+ }
40
+
41
+ export const WalletHeader: React.FC = () => {
42
+ const shouldShowWalletMenu = useInternalWalletManagement()
43
+
44
+ return shouldShowWalletMenu ? (
33
45
  <HeaderAppBar elevation={0} sx={{ justifyContent: 'flex-end' }}>
34
46
  <WalletMenuButton />
35
47
  </HeaderAppBar>
@@ -37,22 +49,27 @@ export const WalletHeader: React.FC = () => {
37
49
  }
38
50
 
39
51
  export const SplitWalletMenuButton: React.FC = () => {
40
- const { hiddenUI } = useWidgetConfig()
41
- const { hasExternalProvider } = useHasExternalWalletProvider()
42
- return !hasExternalProvider && !hiddenUI?.includes(HiddenUI.WalletMenu) ? (
43
- <WalletMenuButton />
44
- ) : null
52
+ const shouldShowWalletMenu = useInternalWalletManagement()
53
+ return shouldShowWalletMenu ? <WalletMenuButton /> : null
45
54
  }
46
55
 
47
56
  export const WalletMenuButton: React.FC = () => {
48
- const { account } = useAccount()
49
57
  const { variant, hiddenUI } = useWidgetConfig()
58
+ const { account, accounts } = useAccount()
59
+
60
+ const [fromChainId] = useFieldValues('fromChain')
61
+ const { chain: fromChain } = useChain(fromChainId)
62
+
63
+ const activeAccount =
64
+ (fromChain
65
+ ? accounts.find((account) => account.chainType === fromChain.chainType)
66
+ : undefined) || account
50
67
 
51
68
  if (variant === 'drawer') {
52
69
  return (
53
70
  <DrawerWalletContainer>
54
- {account.isConnected ? (
55
- <ConnectedButton account={account} />
71
+ {activeAccount.isConnected ? (
72
+ <ConnectedButton account={activeAccount} />
56
73
  ) : (
57
74
  <ConnectButton />
58
75
  )}
@@ -62,8 +79,8 @@ export const WalletMenuButton: React.FC = () => {
62
79
  </DrawerWalletContainer>
63
80
  )
64
81
  }
65
- return account.isConnected ? (
66
- <ConnectedButton account={account} />
82
+ return activeAccount.isConnected ? (
83
+ <ConnectedButton account={activeAccount} />
67
84
  ) : (
68
85
  <ConnectButton />
69
86
  )
@@ -74,7 +91,7 @@ const ConnectButton = () => {
74
91
  const { walletConfig, subvariant, variant } = useWidgetConfig()
75
92
  const { openWalletMenu } = useWalletMenu()
76
93
  const connect = async () => {
77
- if (walletConfig?.onConnect) {
94
+ if (!walletConfig?.usePartialWalletManagement && walletConfig?.onConnect) {
78
95
  walletConfig.onConnect()
79
96
  return
80
97
  }
@@ -1,5 +1,6 @@
1
1
  import { ArrowDownward, ArrowForward } from '@mui/icons-material'
2
2
  import { useAvailableChains } from '../../hooks/useAvailableChains.js'
3
+ import { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'
3
4
  import { useToAddressReset } from '../../hooks/useToAddressReset.js'
4
5
  import { useFieldActions } from '../../stores/form/useFieldActions.js'
5
6
  import { IconCard, ReverseContainer } from './ReverseTokensButton.style.js'
@@ -10,20 +11,38 @@ export const ReverseTokensButton: React.FC<{ vertical?: boolean }> = ({
10
11
  const { setFieldValue, getFieldValues } = useFieldActions()
11
12
  const { getChainById } = useAvailableChains()
12
13
  const { tryResetToAddress } = useToAddressReset()
14
+ const autoPopulateToAddress = useToAddressAutoPopulate()
13
15
 
14
16
  const handleClick = () => {
15
- const [fromChainId, fromToken, toChainId, toToken] = getFieldValues(
16
- 'fromChain',
17
- 'fromToken',
18
- 'toChain',
19
- 'toToken'
20
- )
17
+ const [fromChainId, fromToken, toChainId, toToken, toAddress] =
18
+ getFieldValues(
19
+ 'fromChain',
20
+ 'fromToken',
21
+ 'toChain',
22
+ 'toToken',
23
+ 'toAddress'
24
+ )
21
25
  setFieldValue('fromAmount', '', { isTouched: true })
22
26
  setFieldValue('fromChain', toChainId, { isTouched: true })
23
27
  setFieldValue('fromToken', toToken, { isTouched: true })
24
28
  setFieldValue('toChain', fromChainId, { isTouched: true })
25
29
  setFieldValue('toToken', fromToken, { isTouched: true })
26
30
 
31
+ const autoPopulatedToAddress = autoPopulateToAddress({
32
+ formType: 'from',
33
+ selectedToAddress: toAddress,
34
+ selectedChainId: toChainId,
35
+ selectedOppositeChainId: fromChainId,
36
+ selectedOppositeTokenAddress: fromToken,
37
+ })
38
+
39
+ // Returning early as a compatible connected wallet was found, and toAddress has been populated
40
+ if (autoPopulatedToAddress) {
41
+ return
42
+ }
43
+
44
+ // Auto-population applies in certain scenarios, but otherwise,
45
+ // we attempt to reset `toAddress` when bridging across ecosystems
27
46
  // fromChainId becomes toChainId after using reverse
28
47
  const toChain = getChainById(fromChainId)
29
48
  if (toChain) {
@@ -1,7 +1,7 @@
1
1
  import type { LiFiStepExtended } from '@lifi/sdk'
2
2
  import { AccessTimeFilled } from '@mui/icons-material'
3
3
  import { Box, Tooltip } from '@mui/material'
4
- import { useEffect, useState } from 'react'
4
+ import { type FC, type PropsWithChildren, useEffect, useState } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import { useTimer } from '../../hooks/timer/useTimer.js'
7
7
  import { IconTypography } from '../IconTypography.js'
@@ -78,20 +78,13 @@ export const StepTimer: React.FC<{
78
78
  ? Math.floor(step.estimate.executionDuration)
79
79
  : Math.floor(step.estimate.executionDuration / 60)
80
80
  return (
81
- <Tooltip title={t('tooltip.estimatedTime')} sx={{ cursor: 'help' }}>
82
- <Box component="span" display="flex" alignItems="center">
83
- <IconTypography component="span" mr={0.5} fontSize={16}>
84
- <AccessTimeFilled fontSize="inherit" />
85
- </IconTypography>
86
- <Box component="span">
87
- {duration.toLocaleString(i18n.language, {
88
- style: 'unit',
89
- unit: showSeconds ? 'second' : 'minute',
90
- unitDisplay: 'narrow',
91
- })}
92
- </Box>
93
- </Box>
94
- </Tooltip>
81
+ <StepTimerContent>
82
+ {duration.toLocaleString(i18n.language, {
83
+ style: 'unit',
84
+ unit: showSeconds ? 'second' : 'minute',
85
+ unitDisplay: 'narrow',
86
+ })}
87
+ </StepTimerContent>
95
88
  )
96
89
  }
97
90
 
@@ -108,15 +101,32 @@ export const StepTimer: React.FC<{
108
101
  return isTimerExpired ? (
109
102
  t('main.inProgress')
110
103
  ) : (
104
+ <StepTimerContent>
105
+ {`${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`}
106
+ </StepTimerContent>
107
+ )
108
+ }
109
+
110
+ const StepTimerContent: FC<PropsWithChildren> = ({ children }) => {
111
+ const { t } = useTranslation()
112
+ return (
111
113
  <Tooltip title={t('tooltip.estimatedTime')} sx={{ cursor: 'help' }}>
112
- <Box component="span" display="flex" alignItems="center">
113
- <IconTypography component="span" mr={0.5} fontSize={16}>
114
+ <Box component="span" display="flex" alignItems="center" height={14}>
115
+ <IconTypography
116
+ component="span"
117
+ sx={{ marginRight: 0.5, fontSize: 16 }}
118
+ >
114
119
  <AccessTimeFilled fontSize="inherit" />
115
120
  </IconTypography>
116
121
  <Box
117
122
  component="span"
118
- sx={{ fontVariantNumeric: 'tabular-nums', cursor: 'help' }}
119
- >{`${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`}</Box>
123
+ sx={{
124
+ fontVariantNumeric: 'tabular-nums',
125
+ cursor: 'help',
126
+ }}
127
+ >
128
+ {children}
129
+ </Box>
120
130
  </Box>
121
131
  </Tooltip>
122
132
  )
@@ -26,8 +26,11 @@ export const TokenList: FC<TokenListProps> = ({
26
26
  'tokenSearchFilter'
27
27
  )
28
28
 
29
- const { chain, isLoading: isChainLoading } = useChain(selectedChainId)
30
- const { account } = useAccount({ chainType: chain?.chainType })
29
+ const { chain: selectedChain, isLoading: isSelectedChainLoading } =
30
+ useChain(selectedChainId)
31
+ const { account } = useAccount({
32
+ chainType: selectedChain?.chainType,
33
+ })
31
34
 
32
35
  const {
33
36
  tokens: chainTokens,
@@ -64,7 +67,7 @@ export const TokenList: FC<TokenListProps> = ({
64
67
 
65
68
  const isLoading =
66
69
  isTokensLoading ||
67
- isChainLoading ||
70
+ isSelectedChainLoading ||
68
71
  (tokenSearchEnabled && isSearchedTokenLoading)
69
72
 
70
73
  const tokens = filteredTokens.length
@@ -88,7 +91,7 @@ export const TokenList: FC<TokenListProps> = ({
88
91
  tokens={tokens}
89
92
  scrollElementRef={parentRef}
90
93
  chainId={selectedChainId}
91
- chain={chain}
94
+ chain={selectedChain}
92
95
  isLoading={isLoading}
93
96
  isBalanceLoading={isBalanceLoading}
94
97
  showCategories={showCategories}
@@ -34,7 +34,7 @@ export const TokenListItem: React.FC<TokenListItemProps> = ({
34
34
  }) => {
35
35
  const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
36
36
  e.stopPropagation()
37
- onClick?.(token.address)
37
+ onClick?.(token.address, chain?.id)
38
38
  }
39
39
  return (
40
40
  <ListItem
@@ -20,11 +20,11 @@ export interface VirtualizedTokenListProps {
20
20
  chainId?: number
21
21
  chain?: ExtendedChain
22
22
  showCategories?: boolean
23
- onClick(tokenAddress: string): void
23
+ onClick(tokenAddress: string, chainId?: number): void
24
24
  }
25
25
 
26
26
  export interface TokenListItemBaseProps {
27
- onClick?(tokenAddress: string): void
27
+ onClick?(tokenAddress: string, chainId?: number): void
28
28
  size: number
29
29
  start: number
30
30
  }
@@ -1,4 +1,5 @@
1
1
  import { useCallback } from 'react'
2
+ import { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'
2
3
  import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
3
4
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
4
5
  import { useChainOrderStoreContext } from '../../stores/chains/ChainOrderStore.js'
@@ -9,13 +10,20 @@ import { useFieldController } from '../../stores/form/useFieldController.js'
9
10
  import { WidgetEvent } from '../../types/events.js'
10
11
  import type { DisabledUI } from '../../types/widget.js'
11
12
 
13
+ export type UseTokenSelectArgs = {
14
+ formType: FormType
15
+ onClick?: () => void
16
+ }
17
+
12
18
  export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
13
19
  const { subvariant, disabledUI } = useWidgetConfig()
14
20
  const emitter = useWidgetEvents()
15
21
  const { setFieldValue, getFieldValues } = useFieldActions()
22
+ const autoPopulateToAddress = useToAddressAutoPopulate()
23
+ const chainOrderStore = useChainOrderStoreContext()
24
+
16
25
  const tokenKey = FormKeyHelper.getTokenKey(formType)
17
26
  const { onChange } = useFieldController({ name: tokenKey })
18
- const chainOrderStore = useChainOrderStoreContext()
19
27
 
20
28
  return useCallback(
21
29
  (tokenAddress: string, chainId?: number) => {
@@ -32,13 +40,18 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
32
40
  setFieldValue(amountKey, '')
33
41
  }
34
42
  const oppositeFormType = formType === 'from' ? 'to' : 'from'
35
- const [selectedOppositeToken, selectedOppositeChainId] = getFieldValues(
43
+ const [
44
+ selectedOppositeTokenAddress,
45
+ selectedOppositeChainId,
46
+ selectedToAddress,
47
+ ] = getFieldValues(
36
48
  FormKeyHelper.getTokenKey(oppositeFormType),
37
- FormKeyHelper.getChainKey(oppositeFormType)
49
+ FormKeyHelper.getChainKey(oppositeFormType),
50
+ 'toAddress'
38
51
  )
39
52
  // TODO: remove when we enable same chain/token transfers
40
53
  if (
41
- selectedOppositeToken === tokenAddress &&
54
+ selectedOppositeTokenAddress === tokenAddress &&
42
55
  selectedOppositeChainId === selectedChainId &&
43
56
  subvariant !== 'custom'
44
57
  ) {
@@ -48,20 +61,29 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
48
61
  })
49
62
  }
50
63
 
51
- // Check if the selected source chain matches any chain on the destination chain selection view (chainOrder array).
52
- // If a match exists and the destination token is not selected, update the destination chain to match the source.
53
- if (
54
- formType === 'from' &&
55
- !selectedOppositeToken &&
56
- selectedChainId &&
57
- chainOrderStore.getState().chainOrder.to.includes(selectedChainId)
58
- ) {
59
- setFieldValue(FormKeyHelper.getChainKey('to'), selectedChainId, {
60
- isDirty: true,
61
- isTouched: true,
62
- })
64
+ // If no opposite token is selected, synchronize the opposite chain to match the currently selected chain
65
+ const { setChain } = chainOrderStore.getState()
66
+ if (!selectedOppositeTokenAddress && selectedChainId) {
67
+ setFieldValue(
68
+ FormKeyHelper.getChainKey(oppositeFormType),
69
+ selectedChainId,
70
+ {
71
+ isDirty: true,
72
+ isTouched: true,
73
+ }
74
+ )
75
+ setChain(selectedChainId, oppositeFormType)
63
76
  }
64
77
 
78
+ // Automatically populate toAddress field if bridging across ecosystems and compatible wallet is connected
79
+ autoPopulateToAddress({
80
+ formType,
81
+ selectedToAddress,
82
+ selectedChainId,
83
+ selectedOppositeChainId,
84
+ selectedOppositeTokenAddress,
85
+ })
86
+
65
87
  const eventToEmit =
66
88
  formType === 'from'
67
89
  ? WidgetEvent.SourceChainTokenSelected
@@ -77,6 +99,7 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
77
99
  onClick?.()
78
100
  },
79
101
  [
102
+ autoPopulateToAddress,
80
103
  chainOrderStore,
81
104
  disabledUI,
82
105
  emitter,
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.8.2'
2
+ export const version = '3.10.0'
@@ -5,6 +5,11 @@ import { useCallback } from 'react'
5
5
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
6
6
  import { isItemAllowed } from '../utils/item.js'
7
7
 
8
+ export type GetChainById = (
9
+ chainId?: number,
10
+ chains?: ExtendedChain[]
11
+ ) => ExtendedChain | undefined
12
+
8
13
  const supportedChainTypes = [ChainType.EVM, ChainType.SVM, ChainType.UTXO]
9
14
 
10
15
  export const useAvailableChains = (chainTypes?: ChainType[]) => {
@@ -35,7 +40,7 @@ export const useAvailableChains = (chainTypes?: ChainType[]) => {
35
40
  staleTime: 300_000,
36
41
  })
37
42
 
38
- const getChainById = useCallback(
43
+ const getChainById: GetChainById = useCallback(
39
44
  (chainId?: number, chains: ExtendedChain[] | undefined = data) => {
40
45
  if (!chainId) {
41
46
  return
@@ -9,5 +9,6 @@ export const useChain = (chainId?: number) => {
9
9
  return {
10
10
  chain,
11
11
  isLoading,
12
+ getChainById,
12
13
  }
13
14
  }
@@ -1,14 +1,8 @@
1
- import {
2
- ChainType,
3
- type EVMChain,
4
- type RouteExtended,
5
- type Token,
6
- } from '@lifi/sdk'
1
+ import type { EVMChain, RouteExtended, Token } from '@lifi/sdk'
7
2
  import { useAccount } from '@lifi/wallet-management'
8
3
  import { useQuery } from '@tanstack/react-query'
9
- import type { Address } from 'viem'
10
- import { type Connector, useBytecode } from 'wagmi'
11
4
  import { useAvailableChains } from './useAvailableChains.js'
5
+ import { useIsContractAddress } from './useIsContractAddress.js'
12
6
  import { getTokenBalancesWithRetry } from './useTokenBalance.js'
13
7
 
14
8
  export interface GasSufficiency {
@@ -27,18 +21,12 @@ export const useGasSufficiency = (route?: RouteExtended) => {
27
21
  const { account } = useAccount({
28
22
  chainType: getChainById(route?.fromChainId)?.chainType,
29
23
  })
30
- const { data: contractCode } = useBytecode({
31
- address:
32
- account.chainType === ChainType.EVM
33
- ? (account.address as Address)
34
- : undefined,
35
- query: {
36
- refetchInterval: 300_000,
37
- staleTime: 300_000,
38
- },
39
- })
40
24
 
41
- const isContractAddress = !!contractCode
25
+ const isContractAddress = useIsContractAddress(
26
+ account.address,
27
+ route?.fromChainId,
28
+ account.chainType
29
+ )
42
30
 
43
31
  const { data: insufficientGas, isLoading } = useQuery({
44
32
  queryKey: ['gas-sufficiency-check', account.address, route?.id],
@@ -0,0 +1,21 @@
1
+ import { ChainType } from '@lifi/sdk'
2
+ import type { Address } from 'viem'
3
+ import { useBytecode } from 'wagmi'
4
+
5
+ export const useIsContractAddress = (
6
+ address?: string,
7
+ chainId?: number,
8
+ chainType?: ChainType
9
+ ) => {
10
+ const { data: contractCode } = useBytecode({
11
+ address: address as Address,
12
+ chainId: chainId,
13
+ query: {
14
+ refetchInterval: 300_000,
15
+ staleTime: 300_000,
16
+ enabled: Boolean(chainType === ChainType.EVM && chainId),
17
+ },
18
+ })
19
+ const isContractAddress = !!contractCode
20
+ return isContractAddress
21
+ }