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