@rhinestone/deposit-modal 0.12.2 → 0.13.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 (65) hide show
  1. package/README.md +30 -16
  2. package/dist/ClaimModal-CREZO27I.mjs +11 -0
  3. package/dist/ClaimModal-JRIA32U2.cjs +11 -0
  4. package/dist/{DepositModalReown-TEC3MEZ4.mjs → DepositModalReown-3H75G3XQ.mjs} +44 -22
  5. package/dist/{DepositModalReown-VG66OTIU.cjs → DepositModalReown-YNNHLAV2.cjs} +57 -35
  6. package/dist/{QRCode-WUC652SH.mjs → QRCode-5RGIZZHL.mjs} +1 -1
  7. package/dist/{QRCode-5DXFNKI2.cjs → QRCode-JKXG4RHN.cjs} +2 -2
  8. package/dist/WithdrawModal-NVRX44IZ.mjs +10 -0
  9. package/dist/WithdrawModal-YYSBPCJJ.cjs +10 -0
  10. package/dist/{chunk-OIN4CXCP.cjs → chunk-2G2EIN4A.cjs} +1510 -896
  11. package/dist/{chunk-O7BCVQ6L.cjs → chunk-4KCSH4T3.cjs} +157 -199
  12. package/dist/{chunk-TM7VGHAG.cjs → chunk-5NUIKYSF.cjs} +83 -209
  13. package/dist/{chunk-UQIJU6HV.cjs → chunk-7N77GIP4.cjs} +102 -88
  14. package/dist/{chunk-J46WDSRD.mjs → chunk-COCBCQQC.mjs} +121 -86
  15. package/dist/chunk-CVGNCUVU.cjs +154 -0
  16. package/dist/{chunk-SDSKIJC7.mjs → chunk-DSIZNRWA.mjs} +1540 -926
  17. package/dist/{chunk-75DVGSAX.cjs → chunk-DW2EQA2U.cjs} +6 -3
  18. package/dist/{chunk-FXXUIK4E.mjs → chunk-EOMT5LKX.mjs} +374 -151
  19. package/dist/chunk-FZ5FZFIG.mjs +31 -0
  20. package/dist/chunk-GACIGCSN.cjs +31 -0
  21. package/dist/{chunk-HL5NUKZW.cjs → chunk-HOGZKHHY.cjs} +3 -3
  22. package/dist/{chunk-FLVLYO64.mjs → chunk-LTMMETAB.mjs} +29 -15
  23. package/dist/{chunk-2TPSF77V.mjs → chunk-OLCD75JK.mjs} +20 -146
  24. package/dist/{chunk-PWLEBV5V.cjs → chunk-RAADLTSW.cjs} +240 -205
  25. package/dist/{chunk-4ZXQPFNU.mjs → chunk-RN5VKWI6.mjs} +4 -1
  26. package/dist/{chunk-3STTWRAN.mjs → chunk-VUATPOWJ.mjs} +147 -189
  27. package/dist/{chunk-JE4R5VA3.mjs → chunk-XYJFEN5D.mjs} +1 -1
  28. package/dist/chunk-YREVFB64.mjs +154 -0
  29. package/dist/{chunk-BRYNGLDM.cjs → chunk-ZMT4ATHR.cjs} +437 -214
  30. package/dist/claim.cjs +7 -6
  31. package/dist/claim.d.cts +11 -3
  32. package/dist/claim.d.ts +11 -3
  33. package/dist/claim.mjs +6 -5
  34. package/dist/{constants-FuzG3h4m.d.cts → constants-DBrWhJWE.d.cts} +12 -7
  35. package/dist/{constants-FuzG3h4m.d.ts → constants-DBrWhJWE.d.ts} +12 -7
  36. package/dist/constants.cjs +3 -9
  37. package/dist/constants.d.cts +2 -3
  38. package/dist/constants.d.ts +2 -3
  39. package/dist/constants.mjs +4 -10
  40. package/dist/deposit.cjs +8 -8
  41. package/dist/deposit.d.cts +2 -2
  42. package/dist/deposit.d.ts +2 -2
  43. package/dist/deposit.mjs +7 -7
  44. package/dist/embed.cjs +1268 -0
  45. package/dist/embed.d.cts +177 -0
  46. package/dist/embed.d.ts +177 -0
  47. package/dist/embed.mjs +1268 -0
  48. package/dist/index.cjs +11 -16
  49. package/dist/index.d.cts +3 -3
  50. package/dist/index.d.ts +3 -3
  51. package/dist/index.mjs +14 -19
  52. package/dist/modal-ui-state-OD1RmKkK.d.cts +30 -0
  53. package/dist/modal-ui-state-OD1RmKkK.d.ts +30 -0
  54. package/dist/polymarket.cjs +17 -7
  55. package/dist/polymarket.mjs +13 -3
  56. package/dist/styles.css +124 -98
  57. package/dist/{types-oFblf65g.d.ts → types-B89I8_8H.d.ts} +27 -1
  58. package/dist/{types-BO4iP6fG.d.cts → types-nnDR4tqA.d.cts} +27 -1
  59. package/dist/withdraw.cjs +6 -6
  60. package/dist/withdraw.d.cts +11 -3
  61. package/dist/withdraw.d.ts +11 -3
  62. package/dist/withdraw.mjs +5 -5
  63. package/package.json +33 -1
  64. package/dist/chunk-IAGFDSNC.mjs +0 -11
  65. package/dist/chunk-L32UI7LA.cjs +0 -11
package/dist/embed.mjs ADDED
@@ -0,0 +1,1268 @@
1
+ import {
2
+ DepositModalInner
3
+ } from "./chunk-DSIZNRWA.mjs";
4
+ import {
5
+ markSubmissionUncertain
6
+ } from "./chunk-COCBCQQC.mjs";
7
+ import "./chunk-YREVFB64.mjs";
8
+ import "./chunk-XYJFEN5D.mjs";
9
+ import {
10
+ primeRuntimeChains
11
+ } from "./chunk-EOMT5LKX.mjs";
12
+ import {
13
+ MODAL_VERSION,
14
+ setEmbedHost
15
+ } from "./chunk-FZ5FZFIG.mjs";
16
+ import "./chunk-RN5VKWI6.mjs";
17
+ import {
18
+ HYPERCORE_SPOT_CAIP2,
19
+ getChainName
20
+ } from "./chunk-VUATPOWJ.mjs";
21
+
22
+ // src/embed.tsx
23
+ import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
24
+
25
+ // src/core/bridge/protocol.ts
26
+ var PROTOCOL_VERSION = 2;
27
+ var PAGE_TO_HOST_CHANNEL = "rhinestoneBridge";
28
+ var HOST_TO_PAGE_CHANNEL = "__rhinestone_bridge";
29
+ var MAX_FRAME_LENGTH = 256 * 1024;
30
+ var BRIDGE_METHOD = {
31
+ /** Page→host, first frame of the session. Answers `HelloResult`. */
32
+ HELLO: "hello",
33
+ /** Page→host: a CAIP-27 wallet request, `Caip27Params`. */
34
+ WALLET_REQUEST: "wallet.request",
35
+ /** Page→host: withdraw's transfer. Gated by `CAPABILITY.SEND_TRANSACTION`. */
36
+ SEND_TRANSACTION: "host.sendTransaction",
37
+ /** Page→host: claim's signature. Gated by `CAPABILITY.SIGN_RECOVERY`. */
38
+ SIGN_RECOVERY: "host.signRecovery",
39
+ /** Page→host: show a payment page outside the web view. Gated by
40
+ * `CAPABILITY.OPEN_URL`. */
41
+ OPEN_URL: "host.openUrl"
42
+ };
43
+ var HOST_METHOD = {
44
+ /** Host→page: Android's hardware back and iOS's interactive swipe. Answers
45
+ * `UiBackResult`. */
46
+ BACK: "ui.back"
47
+ };
48
+ var CAPABILITY = {
49
+ SEND_TRANSACTION: BRIDGE_METHOD.SEND_TRANSACTION,
50
+ SIGN_RECOVERY: BRIDGE_METHOD.SIGN_RECOVERY,
51
+ OPEN_URL: BRIDGE_METHOD.OPEN_URL
52
+ };
53
+ var ALLOWED_WALLET_METHODS = [
54
+ "eth_chainId",
55
+ "eth_accounts",
56
+ "eth_sendTransaction",
57
+ "wallet_sendTransaction",
58
+ "eth_signTypedData_v4",
59
+ "wallet_switchEthereumChain"
60
+ ];
61
+ var SUBMITTING_WALLET_METHODS = [
62
+ "eth_sendTransaction",
63
+ "wallet_sendTransaction"
64
+ ];
65
+ var BRIDGE_ERROR_DOMAIN = "bridge";
66
+ var BridgeErrorCode = {
67
+ /** The host has a wallet but cannot reach it — app not installed, session
68
+ * dropped mid-request. Distinct from 4900, which means not connected. */
69
+ WALLET_UNAVAILABLE: 1,
70
+ /**
71
+ * The host cannot say whether the transaction reached the network.
72
+ *
73
+ * An app switch can be killed by the OS between wallet submission and the
74
+ * return trip. Without a code for it the host must lie in one direction or
75
+ * the other, and the safe-looking lie — reporting failure — is the one that
76
+ * double-spends. The page shows its existing "status is uncertain" copy and
77
+ * must not offer a retry.
78
+ */
79
+ SUBMISSION_UNCERTAIN: 2,
80
+ /** The page's own deadline expired. Raised page-side, never sent by a host. */
81
+ REQUEST_TIMEOUT: 3,
82
+ /** The channel went away with the request outstanding. */
83
+ CHANNEL_CLOSED: 4,
84
+ /** The host answered, but not with something the method can return — a null
85
+ * transaction hash, an `eth_accounts` that is not an array. */
86
+ MALFORMED_RESULT: 5
87
+ };
88
+ function bridgeError(code, message, data) {
89
+ return { domain: BRIDGE_ERROR_DOMAIN, code, message, data };
90
+ }
91
+
92
+ // src/core/bridge/channel.ts
93
+ var NoHostChannelError = class extends Error {
94
+ constructor() {
95
+ super(
96
+ `No host channel: window.${PAGE_TO_HOST_CHANNEL}.postMessage is not installed`
97
+ );
98
+ this.name = "NoHostChannelError";
99
+ }
100
+ };
101
+ function readHostChannel(target) {
102
+ const candidate = target[PAGE_TO_HOST_CHANNEL];
103
+ if (!candidate || typeof candidate.postMessage !== "function") {
104
+ return void 0;
105
+ }
106
+ return candidate;
107
+ }
108
+ function createWindowChannel(target, onInvalidFrame) {
109
+ return {
110
+ post(json) {
111
+ const host = readHostChannel(target);
112
+ if (!host) throw new NoHostChannelError();
113
+ host.postMessage(json);
114
+ },
115
+ subscribe(onFrame) {
116
+ const receiver = (frame) => {
117
+ if (typeof frame !== "string") {
118
+ onInvalidFrame?.();
119
+ return;
120
+ }
121
+ onFrame(frame);
122
+ };
123
+ target[HOST_TO_PAGE_CHANNEL] = receiver;
124
+ return () => {
125
+ if (target[HOST_TO_PAGE_CHANNEL] === receiver) {
126
+ delete target[HOST_TO_PAGE_CHANNEL];
127
+ }
128
+ };
129
+ }
130
+ };
131
+ }
132
+
133
+ // src/core/bridge/transport.ts
134
+ import { BaseError } from "viem";
135
+
136
+ // src/core/bridge/envelope.ts
137
+ function isRecord(value) {
138
+ return typeof value === "object" && value !== null && !Array.isArray(value);
139
+ }
140
+ function isFiniteNumber(value) {
141
+ return typeof value === "number" && Number.isFinite(value);
142
+ }
143
+ function isNonEmptyString(value) {
144
+ return typeof value === "string" && value.length > 0;
145
+ }
146
+ function isBridgeError(value) {
147
+ if (!isRecord(value)) return false;
148
+ if (!isFiniteNumber(value.code)) return false;
149
+ if (typeof value.message !== "string") return false;
150
+ if (value.data !== void 0 && typeof value.data !== "string") return false;
151
+ if (value.domain !== void 0 && value.domain !== BRIDGE_ERROR_DOMAIN) {
152
+ return false;
153
+ }
154
+ return true;
155
+ }
156
+ function parseEnvelope(raw) {
157
+ if (raw.length > MAX_FRAME_LENGTH) return { ok: false, reason: "too-large" };
158
+ let value;
159
+ try {
160
+ value = JSON.parse(raw);
161
+ } catch {
162
+ return { ok: false, reason: "not-json" };
163
+ }
164
+ if (!isRecord(value)) return { ok: false, reason: "not-an-object" };
165
+ switch (value.kind) {
166
+ case "event": {
167
+ if (!isNonEmptyString(value.type)) {
168
+ return { ok: false, reason: "bad-event" };
169
+ }
170
+ return { ok: true, envelope: value };
171
+ }
172
+ case "request": {
173
+ if (!isNonEmptyString(value.id) || !isNonEmptyString(value.method)) {
174
+ return { ok: false, reason: "bad-request" };
175
+ }
176
+ return { ok: true, envelope: value };
177
+ }
178
+ case "response": {
179
+ if (!isNonEmptyString(value.id)) {
180
+ return { ok: false, reason: "bad-response" };
181
+ }
182
+ if (value.ok === true) {
183
+ if (!("result" in value)) {
184
+ return { ok: false, reason: "bad-response" };
185
+ }
186
+ return { ok: true, envelope: value };
187
+ }
188
+ if (value.ok === false && isBridgeError(value.error)) {
189
+ return { ok: true, envelope: value };
190
+ }
191
+ return { ok: false, reason: "bad-response" };
192
+ }
193
+ default:
194
+ return { ok: false, reason: "unknown-kind" };
195
+ }
196
+ }
197
+
198
+ // src/core/bridge/transport.ts
199
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
200
+ var USER_APPROVAL_TIMEOUT_MS = 18e4;
201
+ function emptyStats() {
202
+ return {
203
+ "too-large": 0,
204
+ "not-json": 0,
205
+ "not-an-object": 0,
206
+ "unknown-kind": 0,
207
+ "bad-event": 0,
208
+ "bad-request": 0,
209
+ "bad-response": 0,
210
+ "not-a-string": 0,
211
+ "unknown-event": 0,
212
+ "unknown-method": 0,
213
+ "unmatched-response": 0,
214
+ "duplicate-request": 0,
215
+ closed: 0
216
+ };
217
+ }
218
+ var BridgeRequestError = class extends BaseError {
219
+ constructor(error) {
220
+ super(error.message, { name: "BridgeRequestError" });
221
+ this.code = error.code;
222
+ this.domain = error.domain;
223
+ Object.defineProperty(this, "data", {
224
+ value: error.data,
225
+ enumerable: false,
226
+ writable: false
227
+ });
228
+ }
229
+ };
230
+ function isBridgeRequestError(error) {
231
+ return error instanceof BridgeRequestError;
232
+ }
233
+ function isBridgeCode(error, code) {
234
+ return isBridgeRequestError(error) && error.domain === BRIDGE_ERROR_DOMAIN && error.code === code;
235
+ }
236
+ function randomSessionId() {
237
+ const bytes = new Uint8Array(16);
238
+ const webcrypto = globalThis.crypto;
239
+ if (webcrypto?.getRandomValues) {
240
+ webcrypto.getRandomValues(bytes);
241
+ } else {
242
+ for (let i = 0; i < bytes.length; i += 1) {
243
+ bytes[i] = Math.floor(Math.random() * 256);
244
+ }
245
+ }
246
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
247
+ }
248
+ var REPLAY_CACHE_SIZE = 32;
249
+ function createBridgeTransport(options) {
250
+ const { channel } = options;
251
+ const sessionId = options.sessionId ?? randomSessionId();
252
+ const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
253
+ const stats = emptyStats();
254
+ const pending = /* @__PURE__ */ new Map();
255
+ const inbound = /* @__PURE__ */ new Map();
256
+ const handlers = /* @__PURE__ */ new Map();
257
+ const listeners = /* @__PURE__ */ new Map();
258
+ let counter = 0;
259
+ let closed = false;
260
+ const unsubscribe = channel.subscribe(onFrame);
261
+ function drop(reason) {
262
+ stats[reason] += 1;
263
+ }
264
+ function send(frame) {
265
+ channel.post(JSON.stringify(frame));
266
+ }
267
+ function notifyPending() {
268
+ options.onPendingChange?.(pending.size);
269
+ }
270
+ function forgetPending(id) {
271
+ const entry = pending.get(id);
272
+ if (!entry) return void 0;
273
+ pending.delete(id);
274
+ notifyPending();
275
+ return entry;
276
+ }
277
+ function settle(id, settleWith) {
278
+ const entry = forgetPending(id);
279
+ if (!entry) {
280
+ drop("unmatched-response");
281
+ return;
282
+ }
283
+ if (entry.timer !== void 0) clearTimeout(entry.timer);
284
+ settleWith(entry);
285
+ }
286
+ function onFrame(raw) {
287
+ if (closed) {
288
+ drop("closed");
289
+ return;
290
+ }
291
+ const parsed = parseEnvelope(raw);
292
+ if (!parsed.ok) {
293
+ drop(parsed.reason);
294
+ return;
295
+ }
296
+ const envelope = parsed.envelope;
297
+ if (envelope.kind === "response") {
298
+ settle(envelope.id, (entry) => {
299
+ if (envelope.ok) entry.resolve(envelope.result);
300
+ else entry.reject(new BridgeRequestError(envelope.error));
301
+ });
302
+ return;
303
+ }
304
+ if (envelope.kind === "event") {
305
+ const listener = listeners.get(envelope.type);
306
+ if (!listener) {
307
+ drop("unknown-event");
308
+ return;
309
+ }
310
+ listener(envelope.payload);
311
+ return;
312
+ }
313
+ void answer(envelope.id, envelope.method, envelope.params);
314
+ }
315
+ function settleInbound(id, response) {
316
+ inbound.set(id, { status: "settled", response });
317
+ if (inbound.size > REPLAY_CACHE_SIZE) {
318
+ for (const [key, value] of inbound) {
319
+ if (key !== id && value.status === "settled") {
320
+ inbound.delete(key);
321
+ break;
322
+ }
323
+ }
324
+ }
325
+ send(response);
326
+ }
327
+ async function answer(id, method, params) {
328
+ const seen = inbound.get(id);
329
+ if (seen) {
330
+ drop("duplicate-request");
331
+ if (seen.status === "settled") send(seen.response);
332
+ return;
333
+ }
334
+ const handler = handlers.get(method);
335
+ if (!handler) {
336
+ drop("unknown-method");
337
+ send({
338
+ kind: "response",
339
+ id,
340
+ ok: false,
341
+ error: { code: 4200, message: `Unsupported method: ${method}` }
342
+ });
343
+ return;
344
+ }
345
+ inbound.set(id, { status: "running" });
346
+ try {
347
+ const result = await handler(params);
348
+ settleInbound(id, { kind: "response", id, ok: true, result: result ?? null });
349
+ } catch (error) {
350
+ settleInbound(id, {
351
+ kind: "response",
352
+ id,
353
+ ok: false,
354
+ error: {
355
+ code: -32603,
356
+ message: error instanceof Error ? error.message : "Page error"
357
+ }
358
+ });
359
+ }
360
+ }
361
+ return {
362
+ stats,
363
+ request(method, params, requestOptions) {
364
+ if (closed) {
365
+ return Promise.reject(
366
+ new BridgeRequestError(
367
+ bridgeError(
368
+ BridgeErrorCode.CHANNEL_CLOSED,
369
+ "The connection to the app closed."
370
+ )
371
+ )
372
+ );
373
+ }
374
+ counter += 1;
375
+ const id = `${sessionId}.${counter}`;
376
+ const timeoutMs = requestOptions?.timeoutMs ?? defaultTimeoutMs;
377
+ return new Promise((resolve, reject) => {
378
+ const timer = timeoutMs > 0 ? setTimeout(() => {
379
+ forgetPending(id);
380
+ reject(
381
+ new BridgeRequestError(
382
+ requestOptions?.uncertainOnTimeout ? bridgeError(
383
+ BridgeErrorCode.SUBMISSION_UNCERTAIN,
384
+ "The app did not confirm whether the transaction was sent."
385
+ ) : bridgeError(
386
+ BridgeErrorCode.REQUEST_TIMEOUT,
387
+ "The app did not respond."
388
+ )
389
+ )
390
+ );
391
+ }, timeoutMs) : void 0;
392
+ pending.set(id, {
393
+ resolve,
394
+ reject,
395
+ timer
396
+ });
397
+ notifyPending();
398
+ try {
399
+ send({ kind: "request", id, method, params });
400
+ } catch (error) {
401
+ forgetPending(id);
402
+ if (timer !== void 0) clearTimeout(timer);
403
+ reject(
404
+ error instanceof NoHostChannelError ? new BridgeRequestError(
405
+ bridgeError(
406
+ BridgeErrorCode.CHANNEL_CLOSED,
407
+ "The connection to the app closed."
408
+ )
409
+ ) : error
410
+ );
411
+ }
412
+ });
413
+ },
414
+ emit(type, payload) {
415
+ if (closed) return;
416
+ try {
417
+ send({ kind: "event", type, payload });
418
+ } catch {
419
+ }
420
+ },
421
+ handle(method, handler) {
422
+ handlers.set(method, handler);
423
+ },
424
+ on(type, handler) {
425
+ listeners.set(type, handler);
426
+ },
427
+ close() {
428
+ if (closed) return;
429
+ closed = true;
430
+ unsubscribe();
431
+ const closing = new BridgeRequestError(
432
+ bridgeError(
433
+ BridgeErrorCode.CHANNEL_CLOSED,
434
+ "The connection to the app closed."
435
+ )
436
+ );
437
+ for (const [, entry] of pending) {
438
+ if (entry.timer !== void 0) clearTimeout(entry.timer);
439
+ entry.reject(closing);
440
+ }
441
+ pending.clear();
442
+ notifyPending();
443
+ }
444
+ };
445
+ }
446
+
447
+ // src/core/bridge/validate.ts
448
+ function isRecord2(value) {
449
+ return typeof value === "object" && value !== null && !Array.isArray(value);
450
+ }
451
+ var TX_HASH = /^0x[0-9a-fA-F]{64}$/;
452
+ var HEX = /^0x[0-9a-fA-F]+$/;
453
+ function isTxHash(value) {
454
+ return typeof value === "string" && TX_HASH.test(value);
455
+ }
456
+ function isHex(value) {
457
+ return typeof value === "string" && HEX.test(value);
458
+ }
459
+ function optionalString(value) {
460
+ return typeof value === "string" ? value : void 0;
461
+ }
462
+ function parseWalletState(value) {
463
+ if (!isRecord2(value)) return void 0;
464
+ if (typeof value.isReady !== "boolean") return void 0;
465
+ if (typeof value.isConnected !== "boolean") return void 0;
466
+ if (!Array.isArray(value.accounts)) return void 0;
467
+ const accounts = [];
468
+ for (const entry of value.accounts) {
469
+ if (isRecord2(entry) && typeof entry.caip10 === "string") {
470
+ accounts.push({ caip10: entry.caip10 });
471
+ }
472
+ }
473
+ const chainId = typeof value.chainId === "string" ? value.chainId : value.chainId === null ? null : void 0;
474
+ if (chainId === void 0) return void 0;
475
+ return {
476
+ isReady: value.isReady,
477
+ isConnected: value.isConnected,
478
+ accounts,
479
+ chainId,
480
+ icon: optionalString(value.icon),
481
+ name: optionalString(value.name)
482
+ };
483
+ }
484
+ function parseHelloResult(value) {
485
+ if (!isRecord2(value)) return void 0;
486
+ if (typeof value.protocol !== "number" || !Number.isFinite(value.protocol)) {
487
+ return void 0;
488
+ }
489
+ const host = isRecord2(value.host) ? value.host : void 0;
490
+ if (!host) return void 0;
491
+ const platform = host.platform === "ios" || host.platform === "android" ? host.platform : "other";
492
+ const wallet = parseWalletState(value.wallet);
493
+ if (!wallet) return void 0;
494
+ if (!isRecord2(value.config)) return void 0;
495
+ const capabilities = Array.isArray(value.capabilities) ? value.capabilities.filter(
496
+ (entry) => typeof entry === "string"
497
+ ) : [];
498
+ return {
499
+ protocol: value.protocol,
500
+ host: {
501
+ platform,
502
+ app: optionalString(host.app),
503
+ version: optionalString(host.version)
504
+ },
505
+ capabilities,
506
+ config: value.config,
507
+ wallet
508
+ };
509
+ }
510
+
511
+ // src/core/bridge/wallet.ts
512
+ import { createWalletClient, custom } from "viem";
513
+ var ALLOWED = new Set(ALLOWED_WALLET_METHODS);
514
+ var SWITCH_CONFIRMATION_TIMEOUT_MS = 15e3;
515
+ var HASH_PATTERN = /^0x[0-9a-fA-F]{64}$/;
516
+ var HEX_PATTERN = /^0x[0-9a-fA-F]*$/;
517
+ function evmAccount(state) {
518
+ return state.accounts.find((account) => account.caip10.startsWith("eip155:"))?.caip10;
519
+ }
520
+ function evmAddressFrom(state) {
521
+ const caip10 = evmAccount(state);
522
+ if (!caip10) return void 0;
523
+ const parts = caip10.split(":");
524
+ return parts.length === 3 ? parts[2] : void 0;
525
+ }
526
+ function evmChainIdFrom(caip2) {
527
+ if (!caip2) return void 0;
528
+ const parts = caip2.split(":");
529
+ if (parts.length !== 2 || parts[0] !== "eip155") return void 0;
530
+ const id = Number(parts[1]);
531
+ return Number.isInteger(id) && id > 0 ? id : void 0;
532
+ }
533
+ function switchTargetChainId(params) {
534
+ const first = Array.isArray(params) ? params[0] : void 0;
535
+ if (typeof first !== "object" || first === null) return void 0;
536
+ const hex = first.chainId;
537
+ if (typeof hex !== "string") return void 0;
538
+ const id = Number.parseInt(hex, 16);
539
+ return Number.isInteger(id) && id > 0 ? id : void 0;
540
+ }
541
+ function syntheticChain(chainId) {
542
+ return {
543
+ id: chainId,
544
+ name: getChainName(chainId),
545
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
546
+ rpcUrls: { default: { http: [] } }
547
+ };
548
+ }
549
+ function assertResultShape(method, result) {
550
+ const bad = (detail) => {
551
+ const error = new BridgeRequestError(
552
+ bridgeError(
553
+ BridgeErrorCode.MALFORMED_RESULT,
554
+ "The app returned something unexpected. Check your wallet activity before trying again.",
555
+ `${method}: ${detail}`
556
+ )
557
+ );
558
+ throw SUBMITTING_WALLET_METHODS.includes(method) ? markSubmissionUncertain(error) : error;
559
+ };
560
+ switch (method) {
561
+ case "eth_sendTransaction":
562
+ case "wallet_sendTransaction":
563
+ if (typeof result !== "string" || !HASH_PATTERN.test(result)) {
564
+ bad("expected a 32-byte transaction hash");
565
+ }
566
+ return;
567
+ case "eth_signTypedData_v4":
568
+ if (typeof result !== "string" || !HEX_PATTERN.test(result)) {
569
+ bad("expected a hex signature");
570
+ }
571
+ return;
572
+ case "eth_chainId":
573
+ if (typeof result !== "string" || !HEX_PATTERN.test(result)) {
574
+ bad("expected a hex quantity");
575
+ }
576
+ return;
577
+ case "eth_accounts":
578
+ if (!Array.isArray(result)) bad("expected an array of addresses");
579
+ return;
580
+ default:
581
+ return;
582
+ }
583
+ }
584
+ function createBridgedWalletSession(options) {
585
+ let state = options.state;
586
+ const { transport, resolveChain } = options;
587
+ let cachedClient;
588
+ let cachedKey;
589
+ const stateWaiters = /* @__PURE__ */ new Set();
590
+ async function walletRequest(method, params) {
591
+ if (!ALLOWED.has(method)) {
592
+ throw new Error(`Method not supported over the bridge: ${method}`);
593
+ }
594
+ const target = switchTargetChainId(params);
595
+ const chainId = target !== void 0 ? `eip155:${target}` : state.chainId ?? void 0;
596
+ if (!chainId) {
597
+ throw new Error("No chain selected for this wallet request");
598
+ }
599
+ const submitting = SUBMITTING_WALLET_METHODS.includes(method);
600
+ const caip27 = { chainId, request: { method, params } };
601
+ let result;
602
+ try {
603
+ result = await transport.request(
604
+ BRIDGE_METHOD.WALLET_REQUEST,
605
+ caip27,
606
+ { timeoutMs: USER_APPROVAL_TIMEOUT_MS, uncertainOnTimeout: submitting }
607
+ );
608
+ } catch (error) {
609
+ if (submitting && isBridgeRequestError(error) && error.code === -32603) {
610
+ throw markSubmissionUncertain(
611
+ new BridgeRequestError(
612
+ bridgeError(
613
+ BridgeErrorCode.SUBMISSION_UNCERTAIN,
614
+ "The app could not confirm whether the transaction was sent. Check your wallet activity before trying again.",
615
+ error.data
616
+ )
617
+ )
618
+ );
619
+ }
620
+ if (submitting && isBridgeCode(error, BridgeErrorCode.SUBMISSION_UNCERTAIN)) {
621
+ throw markSubmissionUncertain(error);
622
+ }
623
+ throw error;
624
+ }
625
+ assertResultShape(method, result);
626
+ return result;
627
+ }
628
+ const evmTransport = custom({
629
+ async request({ method, params }) {
630
+ return walletRequest(method, params);
631
+ }
632
+ });
633
+ function currentClient() {
634
+ const address = evmAddressFrom(state);
635
+ const chainId = evmChainIdFrom(state.chainId);
636
+ if (!address || chainId === void 0) return void 0;
637
+ const chain = resolveChain(chainId) ?? syntheticChain(chainId);
638
+ const key = `${address}:${chainId}`;
639
+ if (cachedClient && cachedKey === key) return cachedClient;
640
+ cachedClient = createWalletClient({
641
+ account: address,
642
+ chain,
643
+ transport: evmTransport
644
+ });
645
+ cachedKey = key;
646
+ return cachedClient;
647
+ }
648
+ function awaitChain(chainId) {
649
+ if (evmChainIdFrom(state.chainId) === chainId) return Promise.resolve();
650
+ return new Promise((resolve, reject) => {
651
+ let timer;
652
+ const waiter = (next) => {
653
+ if (evmChainIdFrom(next.chainId) !== chainId) return;
654
+ if (timer !== void 0) clearTimeout(timer);
655
+ stateWaiters.delete(waiter);
656
+ resolve();
657
+ };
658
+ timer = setTimeout(() => {
659
+ stateWaiters.delete(waiter);
660
+ reject(
661
+ new Error(
662
+ `Switch to ${resolveChain(chainId)?.name ?? getChainName(chainId)} in your wallet to continue`
663
+ )
664
+ );
665
+ }, SWITCH_CONFIRMATION_TIMEOUT_MS);
666
+ stateWaiters.add(waiter);
667
+ });
668
+ }
669
+ const session = {
670
+ get walletClient() {
671
+ return currentClient();
672
+ },
673
+ get canSignEvm() {
674
+ return state.isConnected && currentClient() !== void 0;
675
+ },
676
+ // Reads fall back to `getPublicClient`; a bridged host has no
677
+ // wallet-backed client to offer.
678
+ publicClient: void 0,
679
+ get address() {
680
+ return evmAddressFrom(state);
681
+ },
682
+ get caipAddress() {
683
+ return evmAccount(state);
684
+ },
685
+ get isConnected() {
686
+ return state.isConnected;
687
+ },
688
+ get isReady() {
689
+ return state.isReady;
690
+ },
691
+ get icon() {
692
+ return state.icon;
693
+ },
694
+ isSolana: false,
695
+ solanaAddress: void 0,
696
+ solanaProvider: void 0,
697
+ solanaConnection: void 0,
698
+ openConnect: () => transport.emit("wallet.connectRequested"),
699
+ disconnect: () => transport.emit("wallet.disconnectRequested"),
700
+ switchChain: async (chainId) => {
701
+ try {
702
+ await walletRequest("wallet_switchEthereumChain", [
703
+ { chainId: `0x${chainId.toString(16)}` }
704
+ ]);
705
+ } catch (error) {
706
+ if (isBridgeRequestError(error)) throw new Error(error.message);
707
+ throw error;
708
+ }
709
+ await awaitChain(chainId);
710
+ }
711
+ };
712
+ return {
713
+ session,
714
+ applyWalletState(next) {
715
+ state = next;
716
+ for (const waiter of [...stateWaiters]) waiter(next);
717
+ options.onStateChange?.(next);
718
+ }
719
+ };
720
+ }
721
+
722
+ // src/core/bridge/connect.ts
723
+ var BridgeHandshakeError = class extends Error {
724
+ constructor(message) {
725
+ super(message);
726
+ this.name = "BridgeHandshakeError";
727
+ }
728
+ };
729
+ async function connectToHost(options) {
730
+ let invalidFrames = 0;
731
+ const channel = createWindowChannel(options.target, () => {
732
+ invalidFrames += 1;
733
+ });
734
+ const transport = createBridgeTransport({
735
+ channel,
736
+ onPendingChange: options.onPendingChange
737
+ });
738
+ const params = {
739
+ protocol: PROTOCOL_VERSION,
740
+ modalVersion: MODAL_VERSION
741
+ };
742
+ let raw;
743
+ try {
744
+ raw = await transport.request(BRIDGE_METHOD.HELLO, params, {
745
+ timeoutMs: options.helloTimeoutMs
746
+ });
747
+ } catch (error) {
748
+ transport.close();
749
+ throw error;
750
+ }
751
+ const hello = parseHelloResult(raw);
752
+ if (!hello) {
753
+ transport.close();
754
+ throw new BridgeHandshakeError(
755
+ invalidFrames > 0 ? "The app answered the handshake with something that was not a string frame." : "The app answered the handshake with an unusable payload."
756
+ );
757
+ }
758
+ let config = hello.config;
759
+ const wallet = createBridgedWalletSession({
760
+ transport,
761
+ state: hello.wallet,
762
+ resolveChain: options.resolveChain,
763
+ onStateChange: options.onWalletState
764
+ });
765
+ transport.on("wallet.state", (payload) => {
766
+ const next = parseWalletState(payload);
767
+ if (next) wallet.applyWalletState(next);
768
+ });
769
+ transport.on("session.configure", (payload) => {
770
+ if (typeof payload !== "object" || payload === null) return;
771
+ config = payload;
772
+ options.onConfig?.(config);
773
+ });
774
+ return {
775
+ transport,
776
+ hello,
777
+ wallet,
778
+ get config() {
779
+ return config;
780
+ },
781
+ close() {
782
+ transport.close();
783
+ }
784
+ };
785
+ }
786
+
787
+ // src/core/bridge/capabilities.ts
788
+ async function requestHostTransfer(transport, params) {
789
+ let result;
790
+ try {
791
+ result = await transport.request(
792
+ BRIDGE_METHOD.SEND_TRANSACTION,
793
+ params,
794
+ { timeoutMs: USER_APPROVAL_TIMEOUT_MS, uncertainOnTimeout: true }
795
+ );
796
+ } catch (error) {
797
+ if (error instanceof Error && isBridgeCode(error, BridgeErrorCode.SUBMISSION_UNCERTAIN)) {
798
+ throw markSubmissionUncertain(error);
799
+ }
800
+ throw error;
801
+ }
802
+ if (!isTxHash(result?.txHash)) {
803
+ throw markSubmissionUncertain(
804
+ new Error(
805
+ "The app did not return a transaction hash. Check your activity before trying again."
806
+ )
807
+ );
808
+ }
809
+ return result.txHash;
810
+ }
811
+ async function requestHostRecoverySignature(transport, params) {
812
+ const result = await transport.request(
813
+ BRIDGE_METHOD.SIGN_RECOVERY,
814
+ params,
815
+ { timeoutMs: USER_APPROVAL_TIMEOUT_MS }
816
+ );
817
+ if (!isHex(result?.signature)) {
818
+ throw new Error("The app did not return a signature.");
819
+ }
820
+ return result.signature;
821
+ }
822
+ async function requestHostBrowser(transport, url) {
823
+ let parsed;
824
+ try {
825
+ parsed = new URL(url);
826
+ } catch {
827
+ throw new Error("The payment page address could not be read.");
828
+ }
829
+ if (parsed.protocol !== "https:") {
830
+ throw new Error("The payment page address is not a secure link.");
831
+ }
832
+ await transport.request(BRIDGE_METHOD.OPEN_URL, {
833
+ url
834
+ });
835
+ }
836
+
837
+ // src/core/bridge/ui-state.ts
838
+ var SETTLING_SCREENS = /* @__PURE__ */ new Set(["processing"]);
839
+ var HOME_SCREENS = /* @__PURE__ */ new Set(["connect", "form", "lookup"]);
840
+ function isHomeScreen(screen) {
841
+ return HOME_SCREENS.has(screen);
842
+ }
843
+ function dismissalFor(input) {
844
+ if (input.walletRequestPending) {
845
+ return {
846
+ state: "blocked",
847
+ reason: "wallet-request-pending",
848
+ message: "Waiting for your wallet."
849
+ };
850
+ }
851
+ if (input.submitting) {
852
+ return {
853
+ state: "blocked",
854
+ reason: "submission-in-flight",
855
+ message: "Confirming your deposit."
856
+ };
857
+ }
858
+ if (SETTLING_SCREENS.has(input.screen)) {
859
+ return {
860
+ state: "blocked",
861
+ reason: "settlement-in-progress",
862
+ message: "Your deposit is on its way."
863
+ };
864
+ }
865
+ return { state: "allowed" };
866
+ }
867
+ function canGoBackNow(input) {
868
+ return dismissalFor(input).state !== "blocked";
869
+ }
870
+ function uiStateFor(input) {
871
+ return { screen: input.screen, dismissal: dismissalFor(input) };
872
+ }
873
+
874
+ // src/core/bridge/viewport.ts
875
+ var VIEWPORT_HEIGHT_PROPERTY = "--rs-embed-viewport-height";
876
+ function trackVisualViewport(win) {
877
+ const target = win;
878
+ const viewport = target?.visualViewport;
879
+ const style = target?.document?.documentElement?.style;
880
+ if (!viewport || !style) return () => {
881
+ };
882
+ const apply = () => {
883
+ style.setProperty(VIEWPORT_HEIGHT_PROPERTY, `${viewport.height}px`);
884
+ };
885
+ apply();
886
+ viewport.addEventListener("resize", apply);
887
+ viewport.addEventListener("scroll", apply);
888
+ return () => {
889
+ viewport.removeEventListener("resize", apply);
890
+ viewport.removeEventListener("scroll", apply);
891
+ style.removeProperty(VIEWPORT_HEIGHT_PROPERTY);
892
+ };
893
+ }
894
+
895
+ // src/core/modal-ui-state.ts
896
+ var UNKNOWN_SCREEN = "unknown";
897
+
898
+ // src/embed.tsx
899
+ import { jsx } from "react/jsx-runtime";
900
+ var WithdrawModal = lazy(
901
+ () => import("./WithdrawModal-NVRX44IZ.mjs").then((m) => ({ default: m.WithdrawModal }))
902
+ );
903
+ var ClaimModal = lazy(
904
+ () => import("./ClaimModal-CREZO27I.mjs").then((m) => ({ default: m.ClaimModal }))
905
+ );
906
+ function missingField(config, keys) {
907
+ const missing = keys.find(
908
+ (key) => typeof config[key] !== "string" || config[key] === ""
909
+ );
910
+ return missing ? {
911
+ code: "EMBED_CONFIG_INVALID",
912
+ message: `The app did not supply ${String(missing)}.`
913
+ } : null;
914
+ }
915
+ var EVM_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
916
+ function isEvmAddress(value) {
917
+ return typeof value === "string" && EVM_ADDRESS.test(value);
918
+ }
919
+ function isEvmChainId(value) {
920
+ return typeof value === "number" && Number.isInteger(value) && value > 0;
921
+ }
922
+ function missingCapability(capabilities, needed, mode) {
923
+ return capabilities.includes(needed) ? null : {
924
+ code: "EMBED_CAPABILITY_MISSING",
925
+ message: `This app cannot run ${mode}: it does not implement ${needed}.`
926
+ };
927
+ }
928
+ function refusal(config, capabilities) {
929
+ switch (config.mode) {
930
+ case "deposit": {
931
+ const missing = missingField(config, [
932
+ "backendUrl",
933
+ "recipient",
934
+ "targetToken"
935
+ ]);
936
+ if (missing) return missing;
937
+ const { targetChain } = config;
938
+ const validTarget = typeof targetChain === "number" && Number.isInteger(targetChain) && targetChain > 0 || targetChain === "solana" || targetChain === HYPERCORE_SPOT_CAIP2;
939
+ return validTarget ? null : {
940
+ code: "EMBED_CONFIG_INVALID",
941
+ message: `The app supplied a targetChain this page cannot resolve: ${JSON.stringify(targetChain)}.`
942
+ };
943
+ }
944
+ case "withdraw": {
945
+ const capability = missingCapability(
946
+ capabilities,
947
+ CAPABILITY.SEND_TRANSACTION,
948
+ "a withdrawal"
949
+ );
950
+ if (capability) return capability;
951
+ const missing = missingField(config, ["backendUrl", "accountAddress"]);
952
+ if (missing) return missing;
953
+ const badWithdrawField = !isEvmAddress(config.accountAddress) && "accountAddress" || !isEvmChainId(config.sourceChain) && "sourceChain" || !isEvmAddress(config.sourceToken) && "sourceToken";
954
+ return badWithdrawField ? {
955
+ code: "EMBED_CONFIG_INVALID",
956
+ message: `The app supplied a ${badWithdrawField} this page cannot use.`
957
+ } : null;
958
+ }
959
+ case "claim": {
960
+ const capability = missingCapability(
961
+ capabilities,
962
+ CAPABILITY.SIGN_RECOVERY,
963
+ "a claim"
964
+ );
965
+ if (capability) return capability;
966
+ return missingField(config, ["backendUrl"]);
967
+ }
968
+ default:
969
+ return {
970
+ code: "EMBED_MODE_UNSUPPORTED",
971
+ message: `This page cannot run in ${String(config.mode ?? "an unnamed")} mode.`
972
+ };
973
+ }
974
+ }
975
+ function sanitizeConfig(config) {
976
+ const str = (value) => typeof value === "string" && value !== "" ? value : void 0;
977
+ const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
978
+ const bool = (value) => typeof value === "boolean" ? value : void 0;
979
+ const arr = (value) => Array.isArray(value) ? value : void 0;
980
+ const obj = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
981
+ const target = (value) => {
982
+ if (typeof value === "number" && Number.isInteger(value) && value > 0) {
983
+ return value;
984
+ }
985
+ return value === "solana" || value === HYPERCORE_SPOT_CAIP2 ? value : void 0;
986
+ };
987
+ return {
988
+ // Raw, because it is the discriminant `refusal` switches on — anything
989
+ // else has to reach it to be named in the error.
990
+ mode: config.mode,
991
+ // Typed like everything else, even though `refusal` also checks them.
992
+ // Required is a property of the MODE, not of the field: `targetToken` and
993
+ // `recipient` are required for a deposit and optional seeds for a
994
+ // withdrawal, so passing them through raw let a withdraw config carry a
995
+ // non-string into `targetToken.toLowerCase()` and take the page down.
996
+ // Dropping first means `refusal` still fails the modes that need them, and
997
+ // the modes that do not simply see an absent seed.
998
+ backendUrl: str(config.backendUrl),
999
+ recipient: str(config.recipient),
1000
+ targetChain: target(config.targetChain),
1001
+ targetToken: str(config.targetToken),
1002
+ sourceChain: num(config.sourceChain),
1003
+ sourceToken: str(config.sourceToken),
1004
+ defaultAmount: str(config.defaultAmount),
1005
+ appBalanceUsd: num(config.appBalanceUsd),
1006
+ outputTokenRules: arr(config.outputTokenRules),
1007
+ rejectUnmapped: bool(config.rejectUnmapped),
1008
+ forceRegister: bool(config.forceRegister),
1009
+ enableWallet: bool(config.enableWallet),
1010
+ enableFiatOnramp: bool(config.enableFiatOnramp),
1011
+ enableQrTransfer: bool(config.enableQrTransfer),
1012
+ enableGaslessDeposit: bool(config.enableGaslessDeposit),
1013
+ enableExchangeConnect: bool(config.enableExchangeConnect),
1014
+ fiatMethods: obj(config.fiatMethods),
1015
+ assetMigrations: obj(config.assetMigrations),
1016
+ initialAssetMigration: str(
1017
+ config.initialAssetMigration
1018
+ ),
1019
+ theme: obj(config.theme),
1020
+ uiConfig: obj(config.uiConfig),
1021
+ debug: bool(config.debug),
1022
+ accountAddress: str(config.accountAddress),
1023
+ defaultTxHash: str(config.defaultTxHash),
1024
+ defaultDestination: str(config.defaultDestination)
1025
+ };
1026
+ }
1027
+ function onrampUnavailable(config, capabilities) {
1028
+ if (config.mode !== "deposit") return null;
1029
+ if (capabilities.includes(CAPABILITY.OPEN_URL)) return null;
1030
+ if (!config.enableFiatOnramp && !config.enableExchangeConnect) return null;
1031
+ return {
1032
+ code: "EMBED_CAPABILITY_MISSING",
1033
+ message: `Fiat and exchange funding are hidden: this app does not implement ${CAPABILITY.OPEN_URL}.`
1034
+ };
1035
+ }
1036
+ function toModalProps(config, canOpenExternally) {
1037
+ return {
1038
+ isOpen: true,
1039
+ // Native draws the sheet. Inline is what stops the page drawing a scrim
1040
+ // and a second one inside it; the class carries back what an inline mount
1041
+ // otherwise loses — safe-area clearance and the input sizing that stops
1042
+ // iOS zooming on focus, both of which the stylesheet scopes to the overlay.
1043
+ inline: true,
1044
+ className: "rs-embed",
1045
+ onClose: () => {
1046
+ },
1047
+ targetChain: config.targetChain,
1048
+ targetToken: config.targetToken,
1049
+ recipient: config.recipient,
1050
+ backendUrl: config.backendUrl,
1051
+ sourceChain: config.sourceChain,
1052
+ sourceToken: config.sourceToken,
1053
+ defaultAmount: config.defaultAmount,
1054
+ appBalanceUsd: config.appBalanceUsd,
1055
+ outputTokenRules: config.outputTokenRules,
1056
+ rejectUnmapped: config.rejectUnmapped,
1057
+ forceRegister: config.forceRegister,
1058
+ enableWallet: config.enableWallet,
1059
+ enableFiatOnramp: canOpenExternally && config.enableFiatOnramp,
1060
+ enableQrTransfer: config.enableQrTransfer,
1061
+ enableGaslessDeposit: config.enableGaslessDeposit,
1062
+ enableExchangeConnect: canOpenExternally && config.enableExchangeConnect,
1063
+ fiatMethods: config.fiatMethods,
1064
+ assetMigrations: config.assetMigrations,
1065
+ initialAssetMigration: config.initialAssetMigration,
1066
+ theme: config.theme,
1067
+ uiConfig: config.uiConfig,
1068
+ debug: config.debug
1069
+ };
1070
+ }
1071
+ function DepositEmbed({ target, onFatal }) {
1072
+ const [session, setSession] = useState(null);
1073
+ const [config, setConfig] = useState(null);
1074
+ const [walletPending, setWalletPending] = useState(false);
1075
+ const pendingRef = useRef(false);
1076
+ const completedRef = useRef(false);
1077
+ const uiRef = useRef({
1078
+ screen: UNKNOWN_SCREEN,
1079
+ submitting: false
1080
+ });
1081
+ const onFatalRef = useRef(onFatal);
1082
+ onFatalRef.current = onFatal;
1083
+ useEffect(
1084
+ () => trackVisualViewport(target ?? globalThis.window),
1085
+ [target]
1086
+ );
1087
+ useEffect(() => {
1088
+ let live = true;
1089
+ let opened = null;
1090
+ let chains = [];
1091
+ void (async () => {
1092
+ try {
1093
+ const next = await connectToHost({
1094
+ target: target ?? globalThis.window,
1095
+ resolveChain: (chainId) => chains.find((chain) => chain.id === chainId),
1096
+ onConfig: (updated) => {
1097
+ if (live) setConfig(sanitizeConfig(updated));
1098
+ },
1099
+ onPendingChange: (pending) => {
1100
+ pendingRef.current = pending > 0;
1101
+ if (live) setWalletPending(pending > 0);
1102
+ }
1103
+ });
1104
+ if (!live) {
1105
+ next.close();
1106
+ return;
1107
+ }
1108
+ opened = next;
1109
+ next.transport.handle(HOST_METHOD.BACK, () => {
1110
+ if (!canGoBackNow({
1111
+ ...uiRef.current,
1112
+ walletRequestPending: pendingRef.current
1113
+ })) {
1114
+ return { handled: true };
1115
+ }
1116
+ const back = uiRef.current.back;
1117
+ back?.();
1118
+ return { handled: Boolean(back) };
1119
+ });
1120
+ setEmbedHost(next.hello.host);
1121
+ setSession(next);
1122
+ setConfig(sanitizeConfig(next.hello.config));
1123
+ void primeRuntimeChains(next.hello.config.backendUrl).then((result) => {
1124
+ chains = result;
1125
+ });
1126
+ } catch (error) {
1127
+ if (live) onFatalRef.current?.(error);
1128
+ }
1129
+ })();
1130
+ return () => {
1131
+ live = false;
1132
+ opened?.close();
1133
+ setEmbedHost(null);
1134
+ };
1135
+ }, [target]);
1136
+ const handleUiState = useCallback(
1137
+ (state) => {
1138
+ if (isHomeScreen(state.screen)) completedRef.current = false;
1139
+ uiRef.current = state;
1140
+ session?.transport.emit(
1141
+ "ui.state",
1142
+ uiStateFor({ ...state, walletRequestPending: walletPending })
1143
+ );
1144
+ },
1145
+ [session, walletPending]
1146
+ );
1147
+ const openExternally = useCallback(
1148
+ async (url) => {
1149
+ if (!session) throw new Error("The app is not connected.");
1150
+ await requestHostBrowser(session.transport, url);
1151
+ },
1152
+ [session]
1153
+ );
1154
+ useEffect(() => {
1155
+ if (!session) return;
1156
+ session.transport.emit(
1157
+ "ui.state",
1158
+ uiStateFor({ ...uiRef.current, walletRequestPending: walletPending })
1159
+ );
1160
+ }, [session, walletPending]);
1161
+ const refused = config && session ? refusal(config, session.hello.capabilities) : null;
1162
+ useEffect(() => {
1163
+ if (!session || !refused) return;
1164
+ session.transport.emit("error", refused);
1165
+ }, [session, refused?.code, refused?.message]);
1166
+ const onrampHidden = config && session && !refused ? onrampUnavailable(config, session.hello.capabilities) : null;
1167
+ useEffect(() => {
1168
+ if (!session || !onrampHidden) return;
1169
+ session.transport.emit("error", onrampHidden);
1170
+ }, [session, onrampHidden?.code, onrampHidden?.message]);
1171
+ if (!session || !config || refused) return null;
1172
+ const ready = config;
1173
+ const { transport } = session;
1174
+ const canOpenExternally = session.hello.capabilities.includes(CAPABILITY.OPEN_URL);
1175
+ const shared = {
1176
+ isOpen: true,
1177
+ inline: true,
1178
+ className: "rs-embed",
1179
+ backendUrl: ready.backendUrl,
1180
+ theme: config.theme,
1181
+ uiConfig: config.uiConfig,
1182
+ debug: config.debug,
1183
+ onReady: () => transport.emit("ready"),
1184
+ onEvent: (event) => transport.emit("analytics", event),
1185
+ onError: (data) => transport.emit("error", data),
1186
+ onClose: () => transport.emit("dismissRequested", {
1187
+ source: completedRef.current ? "flow-complete" : "close-button"
1188
+ })
1189
+ };
1190
+ if (ready.mode === "withdraw") {
1191
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
1192
+ WithdrawModal,
1193
+ {
1194
+ ...shared,
1195
+ accountAddress: ready.accountAddress,
1196
+ sourceChain: ready.sourceChain,
1197
+ sourceToken: ready.sourceToken,
1198
+ targetChain: typeof ready.targetChain === "number" ? ready.targetChain : void 0,
1199
+ targetToken: ready.targetToken,
1200
+ recipient: ready.recipient,
1201
+ defaultAmount: ready.defaultAmount,
1202
+ forceRegister: ready.forceRegister,
1203
+ onSendTransaction: async (request) => ({
1204
+ txHash: await requestHostTransfer(transport, {
1205
+ chainId: request.chainId,
1206
+ token: request.token,
1207
+ amount: request.amount.toString(),
1208
+ to: request.to,
1209
+ from: ready.accountAddress
1210
+ })
1211
+ }),
1212
+ onUiState: handleUiState,
1213
+ onLifecycle: (event) => {
1214
+ if (event.type === "complete") completedRef.current = true;
1215
+ transport.emit("lifecycle", event);
1216
+ }
1217
+ }
1218
+ ) });
1219
+ }
1220
+ if (ready.mode === "claim") {
1221
+ return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
1222
+ ClaimModal,
1223
+ {
1224
+ ...shared,
1225
+ defaultTxHash: ready.defaultTxHash,
1226
+ defaultRefundDestination: ready.defaultDestination,
1227
+ signRecovery: async ({ chainId, signer, depositId, destination }) => (
1228
+ // The fields, not the struct: the host holds the canonical domain
1229
+ // and types, so this cannot become a request to sign anything else.
1230
+ await requestHostRecoverySignature(transport, {
1231
+ chainId,
1232
+ signer,
1233
+ depositId,
1234
+ destination
1235
+ })
1236
+ ),
1237
+ onUiState: handleUiState,
1238
+ onLifecycle: (event) => {
1239
+ if (event.type === "complete") completedRef.current = true;
1240
+ transport.emit("lifecycle", event);
1241
+ }
1242
+ }
1243
+ ) });
1244
+ }
1245
+ return /* @__PURE__ */ jsx(
1246
+ DepositModalInner,
1247
+ {
1248
+ ...toModalProps(ready, canOpenExternally),
1249
+ openExternally: canOpenExternally ? openExternally : void 0,
1250
+ walletSession: session.wallet.session,
1251
+ hasWalletClientProp: false,
1252
+ onUiState: handleUiState,
1253
+ onReady: () => session.transport.emit("ready"),
1254
+ onLifecycle: (event) => {
1255
+ if (event.type === "complete") completedRef.current = true;
1256
+ session.transport.emit("lifecycle", event);
1257
+ },
1258
+ onEvent: (event) => session.transport.emit("analytics", event),
1259
+ onError: (data) => session.transport.emit("error", data),
1260
+ onClose: () => session.transport.emit("dismissRequested", {
1261
+ source: completedRef.current ? "flow-complete" : "close-button"
1262
+ })
1263
+ }
1264
+ );
1265
+ }
1266
+ export {
1267
+ DepositEmbed
1268
+ };