@xswap-link/sdk 0.3.5 → 0.4.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 (199) hide show
  1. package/.eslintrc.json +9 -5
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +2 -1
  4. package/dist/index.d.mts +106 -20
  5. package/dist/index.d.ts +106 -20
  6. package/dist/index.global.js +963 -164
  7. package/dist/index.global.js.map +1 -0
  8. package/dist/index.js +105 -3564
  9. package/dist/index.js.map +1 -0
  10. package/dist/index.mjs +105 -3534
  11. package/dist/index.mjs.map +1 -0
  12. package/nodemon.json +1 -1
  13. package/package.json +16 -9
  14. package/postcss.config.js +8 -1
  15. package/src/{components → assets}/icons/ArrowDownIcon.tsx +1 -2
  16. package/src/assets/icons/ArrowDownLongIcon.tsx +12 -0
  17. package/src/{components → assets}/icons/ArrowLeftIcon.tsx +1 -5
  18. package/src/{components → assets}/icons/ArrowRightIcon.tsx +1 -5
  19. package/src/assets/icons/ArrowUpIcon.tsx +12 -0
  20. package/src/{components → assets}/icons/ArrowUpRightIcon.tsx +1 -5
  21. package/src/assets/icons/CashbacklIcon.tsx +28 -0
  22. package/src/{components → assets}/icons/CheckIcon.tsx +1 -5
  23. package/src/assets/icons/ChevronDownIcon.tsx +12 -0
  24. package/src/assets/icons/ChevronUpIcon.tsx +7 -0
  25. package/src/{components → assets}/icons/CircularProgressIcon.tsx +1 -2
  26. package/src/assets/icons/CloseIcon.tsx +15 -0
  27. package/src/{components → assets}/icons/CoinsIcon.tsx +0 -2
  28. package/src/assets/icons/CopyIcon.tsx +15 -0
  29. package/src/assets/icons/DotGreenIcon.tsx +12 -0
  30. package/src/{components → assets}/icons/DownArrorIcon.tsx +0 -2
  31. package/src/{components → assets}/icons/ErrorIcon.tsx +0 -1
  32. package/src/assets/icons/ErrorModalStatusIcon.tsx +31 -0
  33. package/src/assets/icons/HelpIcon.tsx +21 -0
  34. package/src/assets/icons/HistoryGradientBgIcon.tsx +32 -0
  35. package/src/assets/icons/HistoryIcon.tsx +12 -0
  36. package/src/{components → assets}/icons/HourGlassIcon.tsx +1 -5
  37. package/src/assets/icons/Icon.tsx +5 -0
  38. package/src/{components → assets}/icons/InfoIcon.tsx +0 -1
  39. package/src/assets/icons/LinkExternalIcon.tsx +15 -0
  40. package/src/{components → assets}/icons/PercentageIcon.tsx +0 -1
  41. package/src/assets/icons/RankIcon.tsx +28 -0
  42. package/src/assets/icons/SettingsGradientBgIcon.tsx +32 -0
  43. package/src/assets/icons/SettingsIcon.tsx +12 -0
  44. package/src/{components → assets}/icons/SignIcon.tsx +0 -2
  45. package/src/{components → assets}/icons/SuccessIcon.tsx +0 -1
  46. package/src/assets/icons/SuccessModalStatusIcon.tsx +31 -0
  47. package/src/assets/icons/ThumbUpRoundedIcon.tsx +15 -0
  48. package/src/{components → assets}/icons/TimerIcon.tsx +0 -1
  49. package/src/assets/icons/TokensIcon.tsx +16 -0
  50. package/src/assets/icons/WaitingIcon.tsx +15 -0
  51. package/src/{components → assets}/icons/XMarkIcon.tsx +1 -5
  52. package/src/assets/icons/XPowerIcon.tsx +28 -0
  53. package/src/{components → assets}/icons/index.ts +16 -0
  54. package/src/components/Alert/index.tsx +1 -1
  55. package/src/components/Button/index.tsx +57 -0
  56. package/src/components/MessageBar/SnackMessage.tsx +32 -0
  57. package/src/components/MessageBar/index.tsx +44 -0
  58. package/src/components/Modal/index.tsx +52 -0
  59. package/src/components/PoweredBy/index.tsx +12 -0
  60. package/src/components/SafeInput/index.tsx +51 -0
  61. package/src/components/Skeleton/index.tsx +5 -9
  62. package/src/components/Spinner/index.tsx +6 -8
  63. package/src/components/Swap/Header/Controls/index.tsx +57 -0
  64. package/src/components/Swap/Header/index.tsx +16 -0
  65. package/src/components/Swap/HistoryView/index.tsx +34 -0
  66. package/src/components/Swap/SettingsView/Delivery/index.tsx +42 -0
  67. package/src/components/Swap/SettingsView/Slippage/index.tsx +80 -0
  68. package/src/components/Swap/SettingsView/index.tsx +11 -0
  69. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +13 -0
  70. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +37 -0
  71. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Steps/index.tsx +145 -0
  72. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/SwapPanel/index.tsx +61 -0
  73. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +291 -0
  74. package/src/components/Swap/SwapView/ConfirmationView/TxResult/TokenItem/index.tsx +24 -0
  75. package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +84 -0
  76. package/src/components/Swap/SwapView/ConfirmationView/index.tsx +17 -0
  77. package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +25 -0
  78. package/src/components/Swap/SwapView/FeesPanel/Fee/index.tsx +18 -0
  79. package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +71 -0
  80. package/src/components/Swap/SwapView/FeesPanel/index.tsx +67 -0
  81. package/src/components/Swap/SwapView/SwapButton/index.tsx +154 -0
  82. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +23 -0
  83. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +56 -0
  84. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +70 -0
  85. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +62 -0
  86. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +74 -0
  87. package/src/components/Swap/SwapView/SwapPanel/MaxPanel/index.tsx +35 -0
  88. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +40 -0
  89. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +62 -0
  90. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +399 -0
  91. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +104 -0
  92. package/src/components/Swap/SwapView/SwapPanel/index.tsx +42 -0
  93. package/src/components/Swap/SwapView/index.tsx +20 -0
  94. package/src/components/Swap/index.tsx +34 -0
  95. package/src/components/TextInput/index.tsx +67 -0
  96. package/src/components/Timer/index.tsx +57 -0
  97. package/src/components/ToggleButton/index.tsx +24 -0
  98. package/src/components/{UnknownTokenLogo/UnknownTokenLogo.tsx → TokenLogo/Unknown/index.tsx} +3 -4
  99. package/src/components/TokenLogo/WithChain/index.tsx +34 -0
  100. package/src/components/TokenLogo/index.tsx +22 -0
  101. package/src/components/Tooltip/index.tsx +108 -0
  102. package/src/components/TxConfigForm/index.tsx +63 -42
  103. package/src/components/TxDataCard/TokenDetails/index.tsx +30 -0
  104. package/src/components/TxDataCard/TxDataCardUI/index.tsx +133 -0
  105. package/src/components/TxDataCard/index.tsx +30 -0
  106. package/src/components/TxStatusButton/index.tsx +64 -108
  107. package/src/components/global.css +115 -4
  108. package/src/components/index.ts +18 -1
  109. package/src/config/fonts.ts +114 -0
  110. package/src/config/global.d.ts +4 -0
  111. package/src/config/init.tsx +35 -31
  112. package/src/constants/crosschainEstimationTimes.ts +52 -0
  113. package/src/constants/index.ts +8 -0
  114. package/src/context/GlobalDataProvider.tsx +165 -0
  115. package/src/context/HistoryProvider.tsx +339 -0
  116. package/src/context/ModalProvider.tsx +47 -0
  117. package/src/context/SwapProvider.tsx +664 -0
  118. package/src/context/TransactionProvider.tsx +86 -0
  119. package/src/context/TxUIWrapper.tsx +388 -0
  120. package/src/context/index.ts +5 -0
  121. package/src/contracts/abi/CustomXSwapRouter.json +953 -0
  122. package/src/contracts/abi/StakingStableAPR.json +1438 -0
  123. package/src/contracts/abi/XSwapRouter.json +1242 -0
  124. package/src/contracts/abi/XSwapTokenFeeDistributor.json +350 -0
  125. package/src/contracts/abi/index.ts +8 -0
  126. package/src/contracts/addresses.ts +24 -6
  127. package/src/fonts/Satoshi-Black.woff2 +0 -0
  128. package/src/fonts/Satoshi-BlackItalic.woff2 +0 -0
  129. package/src/fonts/Satoshi-Bold.woff2 +0 -0
  130. package/src/fonts/Satoshi-BoldItalic.woff2 +0 -0
  131. package/src/fonts/Satoshi-Light.woff2 +0 -0
  132. package/src/fonts/Satoshi-LightItalic.woff2 +0 -0
  133. package/src/fonts/Satoshi-Medium.woff2 +0 -0
  134. package/src/fonts/Satoshi-MediumItalic.woff2 +0 -0
  135. package/src/fonts/Satoshi-Regular.woff2 +0 -0
  136. package/src/fonts/Satoshi-RegularItalic.woff2 +0 -0
  137. package/src/fonts/Satoshi-Variable.woff2 +0 -0
  138. package/src/fonts/Satoshi-VariableItalic.woff2 +0 -0
  139. package/src/hooks/index.ts +5 -0
  140. package/src/hooks/networkManagement/clientToSigner.tsx +17 -0
  141. package/src/hooks/networkManagement/useNetworks.ts +51 -0
  142. package/src/hooks/useBatchQuery.ts +46 -0
  143. package/src/hooks/useDebounce.tsx +28 -0
  144. package/src/hooks/useEvmContractApi.ts +85 -0
  145. package/src/hooks/usePortal.ts +79 -0
  146. package/src/hooks/useXPower.ts +58 -0
  147. package/src/models/Addresses.ts +8 -2
  148. package/src/models/TokenData.ts +13 -0
  149. package/src/models/TransactionHistory.ts +2 -0
  150. package/src/models/TxUIWrapper.ts +51 -0
  151. package/src/models/XSwapConfig.ts +1 -0
  152. package/src/models/index.ts +1 -0
  153. package/src/models/payloads/GetBalancesPayload.ts +3 -0
  154. package/src/models/payloads/GetFinishedLeaderboardQuestsPayload.ts +3 -0
  155. package/src/models/payloads/GetLeaderboardChangePayload.ts +4 -0
  156. package/src/models/payloads/{GetSwapTxPayload.ts → ModalIntegrationPayload.ts} +3 -1
  157. package/src/models/payloads/index.ts +5 -2
  158. package/src/services/api.ts +34 -5
  159. package/src/services/integrations/monitoring.ts +49 -37
  160. package/src/services/integrations/transactions.ts +50 -6
  161. package/src/utils/contracts.ts +29 -0
  162. package/src/utils/index.ts +3 -3
  163. package/src/utils/numbers.ts +14 -0
  164. package/src/utils/parseWeb3Error.ts +7 -3
  165. package/tailwind.config.js +15 -5
  166. package/tsconfig.json +7 -7
  167. package/tsup.config.ts +61 -0
  168. package/xswap.config.ts +1 -0
  169. package/dist/index.css +0 -1
  170. package/src/components/TxConfigForm/BalanceComponent.tsx +0 -52
  171. package/src/components/TxConfigForm/Button.tsx +0 -20
  172. package/src/components/TxConfigForm/ChainListElement.tsx +0 -36
  173. package/src/components/TxConfigForm/ConfirmationAmount.tsx +0 -91
  174. package/src/components/TxConfigForm/ConfirmationView/ErrorView.tsx +0 -38
  175. package/src/components/TxConfigForm/ConfirmationView/SuccessView.tsx +0 -159
  176. package/src/components/TxConfigForm/ConfirmationView/index.tsx +0 -149
  177. package/src/components/TxConfigForm/ConnectWalletPrompt.tsx +0 -21
  178. package/src/components/TxConfigForm/Description.tsx +0 -17
  179. package/src/components/TxConfigForm/ErrorField.tsx +0 -19
  180. package/src/components/TxConfigForm/FeesDetails.tsx +0 -117
  181. package/src/components/TxConfigForm/Form.tsx +0 -574
  182. package/src/components/TxConfigForm/History.tsx +0 -131
  183. package/src/components/TxConfigForm/HistoryCard.tsx +0 -182
  184. package/src/components/TxConfigForm/PoweredBy.tsx +0 -12
  185. package/src/components/TxConfigForm/Settings.tsx +0 -143
  186. package/src/components/TxConfigForm/Summary.tsx +0 -105
  187. package/src/components/TxConfigForm/SwapPanel.tsx +0 -185
  188. package/src/components/TxConfigForm/TokenPicker.tsx +0 -176
  189. package/src/components/TxConfigForm/TopBar.tsx +0 -58
  190. package/src/components/TxConfigForm/UsdPrice.tsx +0 -44
  191. package/src/components/icons/ChevronDownIcon.tsx +0 -15
  192. package/src/components/icons/ChevronUpIcon.tsx +0 -10
  193. package/src/components/icons/CloseIcon.tsx +0 -15
  194. package/src/components/icons/HistoryIcon.tsx +0 -16
  195. package/src/components/icons/SettingsIcon.tsx +0 -16
  196. /package/src/{components → assets}/icons/ChainlinkCCIPIcon.tsx +0 -0
  197. /package/src/{components → assets}/icons/SearchIcon.tsx +0 -0
  198. /package/src/{components → assets}/icons/XSwapBadgeIcon.tsx +0 -0
  199. /package/src/{components → assets}/icons/XSwapLogo.tsx +0 -0
@@ -0,0 +1,339 @@
1
+ import { CloseIcon } from "@src/assets/icons";
2
+ import { Button, SnackMessage } from "@src/components";
3
+ import {
4
+ CCIP_EXPLORER,
5
+ FALLBACK_CROSSCHAIN_ESTIMATION_TIME,
6
+ crosschainEstimationTimes,
7
+ } from "@src/constants";
8
+ import { useDebounce } from "@src/hooks";
9
+ import { Transaction, TransactionStatus } from "@src/models";
10
+ import { getHistory } from "@src/services";
11
+ import BigNumberJS from "bignumber.js";
12
+ import { closeSnackbar, useSnackbar } from "notistack";
13
+ import {
14
+ Context,
15
+ ReactNode,
16
+ createContext,
17
+ useCallback,
18
+ useContext,
19
+ useEffect,
20
+ useMemo,
21
+ useState,
22
+ } from "react";
23
+ import { useAccount } from "wagmi";
24
+
25
+ export interface HistoryState {
26
+ history: Transaction[];
27
+ addTransactionToLocalHistory: (
28
+ transaction: Transaction,
29
+ userAddress: string,
30
+ ) => void;
31
+ updateTransactionInLocalHistory: (
32
+ transaction: Transaction,
33
+ userAddress: string,
34
+ ) => void;
35
+ historyLoadedOnce: boolean;
36
+ }
37
+
38
+ const HistoryContext: Context<HistoryState> = createContext<HistoryState>(
39
+ {} as HistoryState,
40
+ );
41
+
42
+ interface HistoryProviderProps {
43
+ children: ReactNode;
44
+ }
45
+
46
+ const PENDING_TXS_LOCAL_STORAGE_KEY = "pending_transactions";
47
+
48
+ type LocalHistory = { [walletAddress: string]: Transaction[] };
49
+
50
+ const getLocalHistory = () => {
51
+ return JSON.parse(
52
+ localStorage.getItem(PENDING_TXS_LOCAL_STORAGE_KEY) || "{}",
53
+ ) as LocalHistory;
54
+ };
55
+
56
+ const setLocalHistory = (data: LocalHistory) => {
57
+ localStorage.setItem(PENDING_TXS_LOCAL_STORAGE_KEY, JSON.stringify(data));
58
+ };
59
+
60
+ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
61
+ const { address } = useAccount();
62
+
63
+ const [history, setHistory] = useState<Transaction[]>([]);
64
+ const [historyLoadedOnce, setHistoryLoadedOnce] = useState(false);
65
+
66
+ const { enqueueSnackbar } = useSnackbar();
67
+ const { debounce: getHistoryDebounce } = useDebounce({
68
+ delayMs: 600,
69
+ });
70
+
71
+ useEffect(() => {
72
+ // @TODO remove this line after some time.
73
+ // It is just to clean up users' browsers from the previous leaderboard implementation.
74
+ localStorage.removeItem("transactions-history");
75
+ }, []);
76
+
77
+ const fetchHistory = useCallback(async () => {
78
+ try {
79
+ if (!address) {
80
+ return;
81
+ }
82
+
83
+ const fetchedHistory = await getHistory({
84
+ walletAddress: address,
85
+ });
86
+ const fetchedTransactions: Transaction[] = [];
87
+
88
+ for (const entry of fetchedHistory.history) {
89
+ if (!entry.source) {
90
+ // In case some processing misalignments that might cause source to be null for a brief moment.
91
+ continue;
92
+ }
93
+
94
+ let status: TransactionStatus;
95
+
96
+ if (entry.failed) {
97
+ status = "REVERTED";
98
+ }
99
+
100
+ if (entry.target) {
101
+ status = "DONE";
102
+ } else {
103
+ status = "IN_PROGRESS";
104
+ }
105
+
106
+ const timestampMs = new Date(entry.source?.blockTime).getTime();
107
+
108
+ let estimatedDeliveryTimestamp = FALLBACK_CROSSCHAIN_ESTIMATION_TIME;
109
+ if (
110
+ new BigNumberJS(entry.source.valueForInstantCcipRecieve || "0").gt(0)
111
+ ) {
112
+ estimatedDeliveryTimestamp = timestampMs + 30 * 1000;
113
+ } else {
114
+ const blockchainEstimate =
115
+ timestampMs +
116
+ crosschainEstimationTimes?.[entry.source.blockchainId]?.[
117
+ entry.source.targetBlockchainId
118
+ ];
119
+
120
+ if (blockchainEstimate) {
121
+ estimatedDeliveryTimestamp = blockchainEstimate;
122
+ }
123
+ }
124
+
125
+ fetchedTransactions.push({
126
+ messageId: entry.messageId,
127
+ hash: entry.source.transactionHash,
128
+ timestamp: timestampMs / 1000,
129
+ sourceChainId: entry.source.blockchainId,
130
+ targetChainId: entry.source.targetBlockchainId,
131
+ amountWei: entry.source.tokenAmount,
132
+ tokenAddress: entry.source.tokenAddress,
133
+ tokenOutAddress:
134
+ entry.target?.tokenAddress || entry.source.tokenOutAddress,
135
+ tokenOutAmount:
136
+ entry.target?.tokenAmount || entry.source.estimatedAmountOut,
137
+ estimatedDeliveryTimestamp,
138
+ status,
139
+ });
140
+ }
141
+
142
+ const txHashToTxFromAPI: Record<string, Transaction> = {};
143
+ for (const transaction of fetchedTransactions) {
144
+ txHashToTxFromAPI[transaction.hash] = transaction;
145
+ }
146
+
147
+ const localHistory = getLocalHistory();
148
+ const newLocalHistory: LocalHistory = {};
149
+
150
+ for (const [walletAddress, transactions] of Object.entries(
151
+ localHistory,
152
+ )) {
153
+ if (
154
+ walletAddress.toLowerCase().toLowerCase() !== address.toLowerCase()
155
+ ) {
156
+ continue;
157
+ }
158
+ const newLocalHistoryForWallet: Transaction[] = [];
159
+
160
+ for (const transaction of transactions) {
161
+ if (!txHashToTxFromAPI[transaction.hash]) {
162
+ // localStorage has fresher data.
163
+ fetchedTransactions.push(transaction);
164
+ newLocalHistoryForWallet.push(transaction);
165
+ continue;
166
+ }
167
+
168
+ if (
169
+ txHashToTxFromAPI[transaction.hash] &&
170
+ transaction.status === "DONE" &&
171
+ txHashToTxFromAPI[transaction.hash]?.status === "IN_PROGRESS"
172
+ ) {
173
+ // If we know on the frontend that the tx is done we should update backend result
174
+ // @ts-ignore
175
+ txHashToTxFromAPI[transaction.hash].status = "DONE";
176
+ }
177
+
178
+ if (
179
+ // @ts-ignore
180
+ txHashToTxFromAPI[transaction.hash].status === "DONE" ||
181
+ // @ts-ignore
182
+ txHashToTxFromAPI[transaction.hash].status === "REVERTED"
183
+ ) {
184
+ // API has the same or newer data.
185
+ continue;
186
+ }
187
+ }
188
+
189
+ newLocalHistory[walletAddress.toLowerCase()] = newLocalHistoryForWallet;
190
+ }
191
+ setLocalHistory(newLocalHistory);
192
+ setHistory(fetchedTransactions.sort((a, b) => b.timestamp - a.timestamp));
193
+ } catch (err) {
194
+ console.error(err);
195
+ enqueueSnackbar(
196
+ <SnackMessage
197
+ message={{
198
+ text: `Failed to fetch history!`,
199
+ variant: "error",
200
+ }}
201
+ />,
202
+ { persist: false },
203
+ );
204
+ } finally {
205
+ setHistoryLoadedOnce(true);
206
+ }
207
+ }, [address, getHistory, enqueueSnackbar]);
208
+
209
+ useEffect(() => {
210
+ if (!historyLoadedOnce) {
211
+ getHistoryDebounce(fetchHistory);
212
+ }
213
+ const timer = setInterval(() => {
214
+ getHistoryDebounce(fetchHistory);
215
+ }, 60000);
216
+ return () => clearInterval(timer);
217
+ }, [
218
+ address,
219
+ fetchHistory,
220
+ getHistory,
221
+ getHistoryDebounce,
222
+ historyLoadedOnce,
223
+ ]);
224
+
225
+ const addTransactionToLocalHistory = useCallback(
226
+ (transaction: Transaction, userAddress: string) => {
227
+ setHistory((prevHistory) => {
228
+ // Remove old tx, if it exists already. It's very unlikely though.
229
+ const clearedPrevHistory = prevHistory.filter(
230
+ (historyTransaction) => historyTransaction.hash !== transaction.hash,
231
+ );
232
+
233
+ const localHistory = getLocalHistory();
234
+ setLocalHistory({
235
+ ...localHistory,
236
+ [userAddress]: [transaction, ...clearedPrevHistory],
237
+ });
238
+
239
+ return [transaction, ...clearedPrevHistory];
240
+ });
241
+ },
242
+ [],
243
+ );
244
+
245
+ const showSnackbarForDoneTx = useCallback(
246
+ (messageId: string) => {
247
+ enqueueSnackbar(
248
+ <SnackMessage
249
+ message={{
250
+ text: `Destination chain transaction mined successfully!`,
251
+ variant: "success",
252
+ action: () => (
253
+ <div className="flex items-center flex-nowrap">
254
+ <Button
255
+ type="button"
256
+ onClick={() => {
257
+ window.open(`${CCIP_EXPLORER}${messageId}`);
258
+ }}
259
+ >
260
+ View
261
+ </Button>
262
+
263
+ <div
264
+ className="transaction-provider__close fill-white leading-[0px]"
265
+ onClick={() => closeSnackbar(messageId)}
266
+ >
267
+ <CloseIcon />
268
+ </div>
269
+ </div>
270
+ ),
271
+ }}
272
+ />,
273
+ { persist: false, key: messageId },
274
+ );
275
+ },
276
+ [enqueueSnackbar],
277
+ );
278
+
279
+ const updateTransactionInLocalHistory = useCallback(
280
+ (transaction: Transaction, userAddress: string) => {
281
+ setHistory((prevHistory) => {
282
+ const newHistory = prevHistory.map((historyTransaction) =>
283
+ historyTransaction.hash === transaction.hash &&
284
+ historyTransaction.status !== "DONE"
285
+ ? transaction
286
+ : historyTransaction,
287
+ );
288
+
289
+ const localHistory = getLocalHistory();
290
+ setLocalHistory({
291
+ ...localHistory,
292
+ [userAddress]: [...newHistory],
293
+ });
294
+
295
+ switch (transaction.status) {
296
+ case "DONE": {
297
+ showSnackbarForDoneTx(transaction.hash);
298
+ break;
299
+ }
300
+ case "REVERTED": {
301
+ // @TODO Add some modal when frontend starts listening for the event
302
+ break;
303
+ }
304
+ }
305
+
306
+ return [...newHistory];
307
+ });
308
+ },
309
+ [showSnackbarForDoneTx],
310
+ );
311
+
312
+ useEffect(() => {
313
+ setHistoryLoadedOnce(false);
314
+ setHistory([]);
315
+ }, [address]);
316
+
317
+ const state = useMemo<HistoryState>(
318
+ () => ({
319
+ history,
320
+ addTransactionToLocalHistory,
321
+ updateTransactionInLocalHistory,
322
+ historyLoadedOnce,
323
+ }),
324
+ [
325
+ history,
326
+ addTransactionToLocalHistory,
327
+ updateTransactionInLocalHistory,
328
+ historyLoadedOnce,
329
+ ],
330
+ );
331
+
332
+ return (
333
+ <HistoryContext.Provider value={state}>{children}</HistoryContext.Provider>
334
+ );
335
+ };
336
+
337
+ export function useHistory(): HistoryState {
338
+ return useContext(HistoryContext);
339
+ }
@@ -0,0 +1,47 @@
1
+ import { createContext, FC, ReactNode, useContext, useState } from "react";
2
+
3
+ interface ModalState {
4
+ openModal: (modalContent: ReactNode) => void;
5
+ closeModal: () => void;
6
+ }
7
+
8
+ const ModalContext = createContext<ModalState>({
9
+ openModal: () => {
10
+ throw new Error(`openModal is not implemented`);
11
+ },
12
+ closeModal: () => {
13
+ throw new Error(`closeModal is not implemented`);
14
+ },
15
+ });
16
+
17
+ export const ModalProvider: FC<{ children: ReactNode }> = ({ children }) => {
18
+ const [content, setContent] = useState<ReactNode | null>(null);
19
+
20
+ const openModal = (modalContent: ReactNode) => {
21
+ setContent(modalContent);
22
+ };
23
+
24
+ const closeModal = () => {
25
+ setContent(null);
26
+ };
27
+
28
+ return (
29
+ <ModalContext.Provider value={{ openModal, closeModal }}>
30
+ {children}
31
+ {content && (
32
+ <div className="top-0 left-0 right-0 bottom-0 bg-[rgba(0,0,0,0.8)] fixed flex items-center justify-center z-10">
33
+ <div
34
+ className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10 bg-x_gray_600 x-border rounded-3xl p-4 w-full min-w-[340px]"
35
+ style={{ maxWidth: "480px" }}
36
+ >
37
+ {content}
38
+ </div>
39
+ </div>
40
+ )}
41
+ </ModalContext.Provider>
42
+ );
43
+ };
44
+
45
+ export const useModal = () => {
46
+ return useContext(ModalContext);
47
+ };