@rhinestone/deposit-modal 0.1.7 → 0.1.9

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.
package/dist/index.cjs CHANGED
@@ -1058,12 +1058,16 @@ function createDepositService(baseUrl) {
1058
1058
  }
1059
1059
  return { tokens: [], totalUsd: 0 };
1060
1060
  },
1061
- async fetchStatus(address) {
1062
- const response = await fetch(apiUrl(`/status/${address}`), {
1063
- method: "GET",
1064
- headers: { "Content-Type": "application/json" },
1065
- cache: "no-store"
1066
- });
1061
+ async fetchStatus(address, txHash) {
1062
+ const txHashParam = encodeURIComponent(txHash.toLowerCase());
1063
+ const response = await fetch(
1064
+ apiUrl(`/status/${address}?txHash=${txHashParam}`),
1065
+ {
1066
+ method: "GET",
1067
+ headers: { "Content-Type": "application/json" },
1068
+ cache: "no-store"
1069
+ }
1070
+ );
1067
1071
  if (!response.ok) {
1068
1072
  return { lastEvent: void 0 };
1069
1073
  }
@@ -2142,6 +2146,34 @@ var INITIAL_POLL_INTERVAL = 3e3;
2142
2146
  var MAX_POLL_INTERVAL = 3e4;
2143
2147
  var BACKOFF_MULTIPLIER = 1.5;
2144
2148
  var PROCESS_TIMEOUT_MS = 10 * 60 * 1e3;
2149
+ function getEventTxHash(event) {
2150
+ if (!event?.type) return void 0;
2151
+ if (event.type === "deposit-received") {
2152
+ const data = event.data;
2153
+ return typeof data?.transactionHash === "string" ? data.transactionHash : void 0;
2154
+ }
2155
+ if (event.type === "bridge-started" || event.type === "bridge-complete") {
2156
+ const data = event.data;
2157
+ const txHash = data?.source?.transactionHash;
2158
+ return typeof txHash === "string" ? txHash : void 0;
2159
+ }
2160
+ if (event.type === "bridge-failed") {
2161
+ const data = event.data;
2162
+ const txHash = data?.deposit?.transactionHash;
2163
+ return typeof txHash === "string" ? txHash : void 0;
2164
+ }
2165
+ if (event.type === "error") {
2166
+ const data = event.data;
2167
+ const txHash = data?.deposit?.transactionHash;
2168
+ return typeof txHash === "string" ? txHash : void 0;
2169
+ }
2170
+ return void 0;
2171
+ }
2172
+ function isEventForTx(event, txHash) {
2173
+ const eventTxHash = getEventTxHash(event);
2174
+ if (!eventTxHash) return false;
2175
+ return eventTxHash.toLowerCase() === txHash.toLowerCase();
2176
+ }
2145
2177
  function formatBridgeFailedMessage(event) {
2146
2178
  const eventData = event?.data ?? {};
2147
2179
  const code = typeof eventData.errorCode === "string" ? eventData.errorCode : void 0;
@@ -2180,6 +2212,7 @@ function ProcessingStep({
2180
2212
  const [elapsedSeconds, setElapsedSeconds] = (0, import_react6.useState)(0);
2181
2213
  const startTimeRef = (0, import_react6.useRef)(Date.now());
2182
2214
  const intervalRef = (0, import_react6.useRef)(null);
2215
+ const processTriggerKeyRef = (0, import_react6.useRef)(null);
2183
2216
  const sameChainAndToken = targetChain === sourceChain && targetToken.toLowerCase() === sourceToken.toLowerCase();
2184
2217
  (0, import_react6.useEffect)(() => {
2185
2218
  startTimeRef.current = Date.now();
@@ -2199,13 +2232,21 @@ function ProcessingStep({
2199
2232
  }
2200
2233
  }, [state.type]);
2201
2234
  (0, import_react6.useEffect)(() => {
2235
+ const triggerKey = `${smartAccount.toLowerCase()}:${txHash.toLowerCase()}`;
2236
+ if (processTriggerKeyRef.current === triggerKey) {
2237
+ return;
2238
+ }
2239
+ processTriggerKeyRef.current = triggerKey;
2240
+ let cancelled = false;
2202
2241
  async function triggerProcess() {
2203
2242
  if (!publicClient) {
2243
+ if (cancelled) return;
2204
2244
  setState({ type: "error", message: "No client for source chain" });
2205
2245
  return;
2206
2246
  }
2207
2247
  if (!sender) {
2208
2248
  const message = "Wallet address unavailable";
2249
+ if (cancelled) return;
2209
2250
  setState({ type: "error", message });
2210
2251
  onError?.(message, "PROCESS_ERROR");
2211
2252
  return;
@@ -2214,6 +2255,7 @@ function ProcessingStep({
2214
2255
  await publicClient.waitForTransactionReceipt({ hash: txHash });
2215
2256
  } catch (error) {
2216
2257
  const message = error instanceof Error ? error.message : "Transaction not confirmed";
2258
+ if (cancelled) return;
2217
2259
  setState({ type: "error", message });
2218
2260
  onError?.(message, "PROCESS_ERROR");
2219
2261
  return;
@@ -2226,6 +2268,7 @@ function ProcessingStep({
2226
2268
  txHash,
2227
2269
  sender
2228
2270
  });
2271
+ if (cancelled) return;
2229
2272
  if (result.message.includes("Funds are already on the target chain")) {
2230
2273
  if (sameChainAndToken) {
2231
2274
  setState({ type: "complete" });
@@ -2242,11 +2285,15 @@ function ProcessingStep({
2242
2285
  setState({ type: "processing" });
2243
2286
  } catch (error) {
2244
2287
  const message = error instanceof Error ? error.message : "Process failed";
2288
+ if (cancelled) return;
2245
2289
  setState({ type: "processing", warning: message });
2246
2290
  onError?.(message, "PROCESS_FALLBACK");
2247
2291
  }
2248
2292
  }
2249
2293
  triggerProcess();
2294
+ return () => {
2295
+ cancelled = true;
2296
+ };
2250
2297
  }, [
2251
2298
  smartAccount,
2252
2299
  txHash,
@@ -2272,38 +2319,48 @@ function ProcessingStep({
2272
2319
  let isMounted = true;
2273
2320
  async function pollStatus() {
2274
2321
  try {
2275
- const data = await service.fetchStatus(smartAccount);
2322
+ const data = await service.fetchStatus(smartAccount, txHash);
2276
2323
  const lastEvent2 = data.lastEvent;
2324
+ const eventMatchesTx = isEventForTx(lastEvent2, txHash);
2325
+ const eventForCurrentTx = eventMatchesTx ? lastEvent2 : void 0;
2277
2326
  if (!isMounted) return;
2278
- if (lastEvent2?.type === "bridge-complete") {
2279
- setState({ type: "complete", lastEvent: lastEvent2 });
2280
- const destinationTxHash2 = lastEvent2.data?.destination?.transactionHash;
2327
+ if (eventForCurrentTx?.type === "bridge-complete") {
2328
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2329
+ const destinationTxHash2 = eventForCurrentTx.data?.destination?.transactionHash;
2281
2330
  onDepositComplete?.(txHash, destinationTxHash2);
2282
2331
  return;
2283
2332
  }
2284
- if (!waitForFinalTx && lastEvent2?.type === "bridge-started") {
2285
- setState({ type: "complete", lastEvent: lastEvent2 });
2333
+ if (!waitForFinalTx && eventForCurrentTx?.type === "bridge-started") {
2334
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2286
2335
  onDepositComplete?.(txHash);
2287
2336
  return;
2288
2337
  }
2289
- if (lastEvent2?.type === "deposit-received" && sameChainAndToken) {
2290
- setState({ type: "complete", lastEvent: lastEvent2 });
2338
+ if (eventForCurrentTx?.type === "deposit-received" && sameChainAndToken) {
2339
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2291
2340
  onDepositComplete?.(txHash);
2292
2341
  return;
2293
2342
  }
2294
- if (lastEvent2?.type === "bridge-failed") {
2295
- const formatted = formatBridgeFailedMessage(lastEvent2);
2296
- setState({ type: "failed", message: formatted.message, lastEvent: lastEvent2 });
2343
+ if (eventForCurrentTx?.type === "bridge-failed") {
2344
+ const formatted = formatBridgeFailedMessage(eventForCurrentTx);
2345
+ setState({
2346
+ type: "failed",
2347
+ message: formatted.message,
2348
+ lastEvent: eventForCurrentTx
2349
+ });
2297
2350
  onDepositFailed?.(txHash, formatted.message);
2298
2351
  return;
2299
2352
  }
2300
- if (lastEvent2?.type === "error") {
2301
- const errorMessage = lastEvent2.data?.message ?? "Unknown error";
2302
- setState({ type: "failed", message: errorMessage, lastEvent: lastEvent2 });
2353
+ if (eventForCurrentTx?.type === "error") {
2354
+ const errorMessage = eventForCurrentTx.data?.message ?? "Unknown error";
2355
+ setState({
2356
+ type: "failed",
2357
+ message: errorMessage,
2358
+ lastEvent: eventForCurrentTx
2359
+ });
2303
2360
  onDepositFailed?.(txHash, errorMessage);
2304
2361
  return;
2305
2362
  }
2306
- setState({ type: "processing", lastEvent: lastEvent2 });
2363
+ setState({ type: "processing", lastEvent: eventForCurrentTx });
2307
2364
  scheduleNextPoll();
2308
2365
  } catch {
2309
2366
  scheduleNextPoll();
package/dist/index.mjs CHANGED
@@ -1020,12 +1020,16 @@ function createDepositService(baseUrl) {
1020
1020
  }
1021
1021
  return { tokens: [], totalUsd: 0 };
1022
1022
  },
1023
- async fetchStatus(address) {
1024
- const response = await fetch(apiUrl(`/status/${address}`), {
1025
- method: "GET",
1026
- headers: { "Content-Type": "application/json" },
1027
- cache: "no-store"
1028
- });
1023
+ async fetchStatus(address, txHash) {
1024
+ const txHashParam = encodeURIComponent(txHash.toLowerCase());
1025
+ const response = await fetch(
1026
+ apiUrl(`/status/${address}?txHash=${txHashParam}`),
1027
+ {
1028
+ method: "GET",
1029
+ headers: { "Content-Type": "application/json" },
1030
+ cache: "no-store"
1031
+ }
1032
+ );
1029
1033
  if (!response.ok) {
1030
1034
  return { lastEvent: void 0 };
1031
1035
  }
@@ -2104,6 +2108,34 @@ var INITIAL_POLL_INTERVAL = 3e3;
2104
2108
  var MAX_POLL_INTERVAL = 3e4;
2105
2109
  var BACKOFF_MULTIPLIER = 1.5;
2106
2110
  var PROCESS_TIMEOUT_MS = 10 * 60 * 1e3;
2111
+ function getEventTxHash(event) {
2112
+ if (!event?.type) return void 0;
2113
+ if (event.type === "deposit-received") {
2114
+ const data = event.data;
2115
+ return typeof data?.transactionHash === "string" ? data.transactionHash : void 0;
2116
+ }
2117
+ if (event.type === "bridge-started" || event.type === "bridge-complete") {
2118
+ const data = event.data;
2119
+ const txHash = data?.source?.transactionHash;
2120
+ return typeof txHash === "string" ? txHash : void 0;
2121
+ }
2122
+ if (event.type === "bridge-failed") {
2123
+ const data = event.data;
2124
+ const txHash = data?.deposit?.transactionHash;
2125
+ return typeof txHash === "string" ? txHash : void 0;
2126
+ }
2127
+ if (event.type === "error") {
2128
+ const data = event.data;
2129
+ const txHash = data?.deposit?.transactionHash;
2130
+ return typeof txHash === "string" ? txHash : void 0;
2131
+ }
2132
+ return void 0;
2133
+ }
2134
+ function isEventForTx(event, txHash) {
2135
+ const eventTxHash = getEventTxHash(event);
2136
+ if (!eventTxHash) return false;
2137
+ return eventTxHash.toLowerCase() === txHash.toLowerCase();
2138
+ }
2107
2139
  function formatBridgeFailedMessage(event) {
2108
2140
  const eventData = event?.data ?? {};
2109
2141
  const code = typeof eventData.errorCode === "string" ? eventData.errorCode : void 0;
@@ -2142,6 +2174,7 @@ function ProcessingStep({
2142
2174
  const [elapsedSeconds, setElapsedSeconds] = useState5(0);
2143
2175
  const startTimeRef = useRef4(Date.now());
2144
2176
  const intervalRef = useRef4(null);
2177
+ const processTriggerKeyRef = useRef4(null);
2145
2178
  const sameChainAndToken = targetChain === sourceChain && targetToken.toLowerCase() === sourceToken.toLowerCase();
2146
2179
  useEffect5(() => {
2147
2180
  startTimeRef.current = Date.now();
@@ -2161,13 +2194,21 @@ function ProcessingStep({
2161
2194
  }
2162
2195
  }, [state.type]);
2163
2196
  useEffect5(() => {
2197
+ const triggerKey = `${smartAccount.toLowerCase()}:${txHash.toLowerCase()}`;
2198
+ if (processTriggerKeyRef.current === triggerKey) {
2199
+ return;
2200
+ }
2201
+ processTriggerKeyRef.current = triggerKey;
2202
+ let cancelled = false;
2164
2203
  async function triggerProcess() {
2165
2204
  if (!publicClient) {
2205
+ if (cancelled) return;
2166
2206
  setState({ type: "error", message: "No client for source chain" });
2167
2207
  return;
2168
2208
  }
2169
2209
  if (!sender) {
2170
2210
  const message = "Wallet address unavailable";
2211
+ if (cancelled) return;
2171
2212
  setState({ type: "error", message });
2172
2213
  onError?.(message, "PROCESS_ERROR");
2173
2214
  return;
@@ -2176,6 +2217,7 @@ function ProcessingStep({
2176
2217
  await publicClient.waitForTransactionReceipt({ hash: txHash });
2177
2218
  } catch (error) {
2178
2219
  const message = error instanceof Error ? error.message : "Transaction not confirmed";
2220
+ if (cancelled) return;
2179
2221
  setState({ type: "error", message });
2180
2222
  onError?.(message, "PROCESS_ERROR");
2181
2223
  return;
@@ -2188,6 +2230,7 @@ function ProcessingStep({
2188
2230
  txHash,
2189
2231
  sender
2190
2232
  });
2233
+ if (cancelled) return;
2191
2234
  if (result.message.includes("Funds are already on the target chain")) {
2192
2235
  if (sameChainAndToken) {
2193
2236
  setState({ type: "complete" });
@@ -2204,11 +2247,15 @@ function ProcessingStep({
2204
2247
  setState({ type: "processing" });
2205
2248
  } catch (error) {
2206
2249
  const message = error instanceof Error ? error.message : "Process failed";
2250
+ if (cancelled) return;
2207
2251
  setState({ type: "processing", warning: message });
2208
2252
  onError?.(message, "PROCESS_FALLBACK");
2209
2253
  }
2210
2254
  }
2211
2255
  triggerProcess();
2256
+ return () => {
2257
+ cancelled = true;
2258
+ };
2212
2259
  }, [
2213
2260
  smartAccount,
2214
2261
  txHash,
@@ -2234,38 +2281,48 @@ function ProcessingStep({
2234
2281
  let isMounted = true;
2235
2282
  async function pollStatus() {
2236
2283
  try {
2237
- const data = await service.fetchStatus(smartAccount);
2284
+ const data = await service.fetchStatus(smartAccount, txHash);
2238
2285
  const lastEvent2 = data.lastEvent;
2286
+ const eventMatchesTx = isEventForTx(lastEvent2, txHash);
2287
+ const eventForCurrentTx = eventMatchesTx ? lastEvent2 : void 0;
2239
2288
  if (!isMounted) return;
2240
- if (lastEvent2?.type === "bridge-complete") {
2241
- setState({ type: "complete", lastEvent: lastEvent2 });
2242
- const destinationTxHash2 = lastEvent2.data?.destination?.transactionHash;
2289
+ if (eventForCurrentTx?.type === "bridge-complete") {
2290
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2291
+ const destinationTxHash2 = eventForCurrentTx.data?.destination?.transactionHash;
2243
2292
  onDepositComplete?.(txHash, destinationTxHash2);
2244
2293
  return;
2245
2294
  }
2246
- if (!waitForFinalTx && lastEvent2?.type === "bridge-started") {
2247
- setState({ type: "complete", lastEvent: lastEvent2 });
2295
+ if (!waitForFinalTx && eventForCurrentTx?.type === "bridge-started") {
2296
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2248
2297
  onDepositComplete?.(txHash);
2249
2298
  return;
2250
2299
  }
2251
- if (lastEvent2?.type === "deposit-received" && sameChainAndToken) {
2252
- setState({ type: "complete", lastEvent: lastEvent2 });
2300
+ if (eventForCurrentTx?.type === "deposit-received" && sameChainAndToken) {
2301
+ setState({ type: "complete", lastEvent: eventForCurrentTx });
2253
2302
  onDepositComplete?.(txHash);
2254
2303
  return;
2255
2304
  }
2256
- if (lastEvent2?.type === "bridge-failed") {
2257
- const formatted = formatBridgeFailedMessage(lastEvent2);
2258
- setState({ type: "failed", message: formatted.message, lastEvent: lastEvent2 });
2305
+ if (eventForCurrentTx?.type === "bridge-failed") {
2306
+ const formatted = formatBridgeFailedMessage(eventForCurrentTx);
2307
+ setState({
2308
+ type: "failed",
2309
+ message: formatted.message,
2310
+ lastEvent: eventForCurrentTx
2311
+ });
2259
2312
  onDepositFailed?.(txHash, formatted.message);
2260
2313
  return;
2261
2314
  }
2262
- if (lastEvent2?.type === "error") {
2263
- const errorMessage = lastEvent2.data?.message ?? "Unknown error";
2264
- setState({ type: "failed", message: errorMessage, lastEvent: lastEvent2 });
2315
+ if (eventForCurrentTx?.type === "error") {
2316
+ const errorMessage = eventForCurrentTx.data?.message ?? "Unknown error";
2317
+ setState({
2318
+ type: "failed",
2319
+ message: errorMessage,
2320
+ lastEvent: eventForCurrentTx
2321
+ });
2265
2322
  onDepositFailed?.(txHash, errorMessage);
2266
2323
  return;
2267
2324
  }
2268
- setState({ type: "processing", lastEvent: lastEvent2 });
2325
+ setState({ type: "processing", lastEvent: eventForCurrentTx });
2269
2326
  scheduleNextPoll();
2270
2327
  } catch {
2271
2328
  scheduleNextPoll();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinestone/deposit-modal",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "React modal component for Rhinestone cross-chain deposits",
5
5
  "author": "Rhinestone <dev@rhinestone.wtf>",
6
6
  "bugs": {