@swype-org/deposit 0.3.13 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -91,6 +91,396 @@ function parseCloseRequest(data) {
91
91
  return { type: "blink:close-request" };
92
92
  }
93
93
 
94
+ // src/walletBridge/discover.ts
95
+ function createWalletDiscoverer() {
96
+ if (typeof window === "undefined") {
97
+ return makeNoopHandle();
98
+ }
99
+ const registry = /* @__PURE__ */ new Map();
100
+ const listeners = /* @__PURE__ */ new Set();
101
+ let destroyed = false;
102
+ const onAnnounce = (event) => {
103
+ if (destroyed) return;
104
+ const detail = event.detail;
105
+ if (!isValidProviderDetail(detail)) {
106
+ console.info("[blink-bridge:parent] dropping malformed EIP-6963 announcement", {
107
+ rawInfo: event.detail && event.detail.info
108
+ });
109
+ return;
110
+ }
111
+ const existing = registry.get(detail.info.uuid);
112
+ if (existing && existing.provider === detail.provider) {
113
+ return;
114
+ }
115
+ console.info("[blink-bridge:parent] EIP-6963 announceProvider", {
116
+ rdns: detail.info.rdns,
117
+ name: detail.info.name,
118
+ uuid: detail.info.uuid
119
+ });
120
+ registry.set(detail.info.uuid, {
121
+ info: { ...detail.info },
122
+ provider: detail.provider
123
+ });
124
+ notify();
125
+ };
126
+ window.addEventListener("eip6963:announceProvider", onAnnounce);
127
+ requestProviders();
128
+ const RESWEEP_INTERVALS_MS = [500, 1500, 3e3, 6e3];
129
+ const reSweepTimers = [];
130
+ for (const ms of RESWEEP_INTERVALS_MS) {
131
+ reSweepTimers.push(
132
+ setTimeout(() => {
133
+ if (!destroyed) requestProviders();
134
+ }, ms)
135
+ );
136
+ }
137
+ function requestProviders() {
138
+ try {
139
+ window.dispatchEvent(new Event("eip6963:requestProvider"));
140
+ } catch {
141
+ }
142
+ }
143
+ function notify() {
144
+ const snapshot = list();
145
+ for (const listener of listeners) {
146
+ try {
147
+ listener(snapshot);
148
+ } catch {
149
+ }
150
+ }
151
+ }
152
+ function list() {
153
+ return [...registry.values()];
154
+ }
155
+ return {
156
+ list,
157
+ get(uuid) {
158
+ return registry.get(uuid)?.provider;
159
+ },
160
+ subscribe(listener) {
161
+ listeners.add(listener);
162
+ return () => {
163
+ listeners.delete(listener);
164
+ };
165
+ },
166
+ requestProviders,
167
+ destroy() {
168
+ if (destroyed) return;
169
+ destroyed = true;
170
+ window.removeEventListener("eip6963:announceProvider", onAnnounce);
171
+ for (const t of reSweepTimers) clearTimeout(t);
172
+ registry.clear();
173
+ listeners.clear();
174
+ }
175
+ };
176
+ }
177
+ function makeNoopHandle() {
178
+ return {
179
+ list: () => [],
180
+ get: () => void 0,
181
+ subscribe: () => () => {
182
+ },
183
+ requestProviders: () => {
184
+ },
185
+ destroy: () => {
186
+ }
187
+ };
188
+ }
189
+ function isValidProviderDetail(value) {
190
+ if (!value || typeof value !== "object") return false;
191
+ const detail = value;
192
+ const info = detail.info;
193
+ const provider = detail.provider;
194
+ if (!info || !provider) return false;
195
+ if (typeof info.uuid !== "string" || info.uuid.length === 0) return false;
196
+ if (typeof info.name !== "string") return false;
197
+ if (typeof info.rdns !== "string") return false;
198
+ if (typeof provider.request !== "function") return false;
199
+ return true;
200
+ }
201
+
202
+ // src/walletBridge/protocol.ts
203
+ var BRIDGE_PROTOCOL_VERSION = 1;
204
+ var ALLOWED_RPC_METHODS = /* @__PURE__ */ new Set([
205
+ // Read
206
+ "eth_accounts",
207
+ "eth_blockNumber",
208
+ "eth_call",
209
+ "eth_chainId",
210
+ "eth_estimateGas",
211
+ "eth_gasPrice",
212
+ "eth_getBalance",
213
+ "eth_getCode",
214
+ "eth_getStorageAt",
215
+ "eth_getTransactionByHash",
216
+ "eth_getTransactionCount",
217
+ "eth_getTransactionReceipt",
218
+ "net_version",
219
+ "wallet_getCapabilities",
220
+ "wallet_getCallsStatus",
221
+ // Write / sign
222
+ "eth_requestAccounts",
223
+ "eth_sendTransaction",
224
+ "eth_sendRawTransaction",
225
+ "eth_sign",
226
+ "personal_sign",
227
+ "eth_signTypedData",
228
+ "eth_signTypedData_v3",
229
+ "eth_signTypedData_v4",
230
+ "wallet_addEthereumChain",
231
+ "wallet_switchEthereumChain",
232
+ "wallet_sendCalls",
233
+ "wallet_watchAsset"
234
+ ]);
235
+ var FORWARDED_PROVIDER_EVENTS = [
236
+ "accountsChanged",
237
+ "chainChanged",
238
+ "connect",
239
+ "disconnect",
240
+ "message"
241
+ ];
242
+ function parseBridgeMessage(data) {
243
+ if (!data || typeof data !== "object") return null;
244
+ const msg = data;
245
+ if (msg.protocolVersion !== BRIDGE_PROTOCOL_VERSION) return null;
246
+ switch (msg.type) {
247
+ case "blink:bridge-hello":
248
+ return msg;
249
+ case "blink:wallets-advertised":
250
+ return Array.isArray(msg.wallets) ? msg : null;
251
+ case "blink:rpc-request":
252
+ if (typeof msg.uuid !== "string" || typeof msg.id !== "string" || typeof msg.method !== "string") {
253
+ return null;
254
+ }
255
+ return msg;
256
+ case "blink:rpc-response":
257
+ if (typeof msg.id !== "string") return null;
258
+ return msg;
259
+ case "blink:rpc-event":
260
+ if (typeof msg.uuid !== "string" || typeof msg.event !== "string" || !Array.isArray(msg.args)) {
261
+ return null;
262
+ }
263
+ return msg;
264
+ case "blink:resolve-by-flag":
265
+ if (typeof msg.id !== "string" || !Array.isArray(msg.flags)) return null;
266
+ if (!msg.flags.every((f) => typeof f === "string" && f.length > 0)) return null;
267
+ return msg;
268
+ case "blink:resolve-by-flag-response":
269
+ if (typeof msg.id !== "string") return null;
270
+ if (msg.match !== null && (typeof msg.match !== "object" || !msg.match)) return null;
271
+ return msg;
272
+ default:
273
+ return null;
274
+ }
275
+ }
276
+
277
+ // src/walletBridge/rpcHost.ts
278
+ function attachRpcHost(options) {
279
+ const { iframeWindow, iframeOrigin, discoverer } = options;
280
+ const log = options.log ?? noopLog;
281
+ if (typeof window === "undefined") {
282
+ return { detach: () => {
283
+ } };
284
+ }
285
+ let detached = false;
286
+ const subscriptions = /* @__PURE__ */ new Map();
287
+ const unsubscribeFromDiscoverer = discoverer.subscribe((snapshot) => {
288
+ if (detached) return;
289
+ advertiseWallets(snapshot);
290
+ });
291
+ function postToIframe(message) {
292
+ if (detached) return;
293
+ try {
294
+ iframeWindow.postMessage(message, iframeOrigin);
295
+ } catch (err) {
296
+ log("postMessage to iframe failed", {
297
+ error: err instanceof Error ? err.message : String(err)
298
+ });
299
+ }
300
+ }
301
+ function advertiseWallets(snapshot) {
302
+ const wallets = snapshot.map((entry) => ({
303
+ uuid: entry.info.uuid,
304
+ rdns: entry.info.rdns,
305
+ name: entry.info.name,
306
+ icon: entry.info.icon
307
+ }));
308
+ console.info("[blink-bridge:parent] advertising wallets", wallets.map((w) => ({
309
+ rdns: w.rdns,
310
+ name: w.name
311
+ })));
312
+ const message = {
313
+ type: "blink:wallets-advertised",
314
+ protocolVersion: BRIDGE_PROTOCOL_VERSION,
315
+ wallets
316
+ };
317
+ postToIframe(message);
318
+ }
319
+ function ensureSubscription(uuid, provider) {
320
+ if (subscriptions.has(uuid)) return;
321
+ if (typeof provider.on !== "function") {
322
+ subscriptions.set(uuid, { provider, listeners: /* @__PURE__ */ new Map() });
323
+ return;
324
+ }
325
+ const record = { provider, listeners: /* @__PURE__ */ new Map() };
326
+ for (const event of FORWARDED_PROVIDER_EVENTS) {
327
+ const listener = (...args) => {
328
+ const eventMessage = {
329
+ type: "blink:rpc-event",
330
+ protocolVersion: BRIDGE_PROTOCOL_VERSION,
331
+ uuid,
332
+ event,
333
+ args
334
+ };
335
+ postToIframe(eventMessage);
336
+ };
337
+ try {
338
+ provider.on(event, listener);
339
+ record.listeners.set(event, listener);
340
+ } catch (err) {
341
+ log(`provider.on('${event}') threw \u2014 skipping that event`, {
342
+ uuid,
343
+ error: err instanceof Error ? err.message : String(err)
344
+ });
345
+ }
346
+ }
347
+ subscriptions.set(uuid, record);
348
+ }
349
+ function unsubscribeAll() {
350
+ for (const [, record] of subscriptions) {
351
+ const provider = record.provider;
352
+ if (typeof provider.removeListener !== "function") continue;
353
+ for (const [event, listener] of record.listeners) {
354
+ try {
355
+ provider.removeListener(event, listener);
356
+ } catch {
357
+ }
358
+ }
359
+ }
360
+ subscriptions.clear();
361
+ }
362
+ function sendError(id, code, message, data) {
363
+ const response = {
364
+ type: "blink:rpc-response",
365
+ protocolVersion: BRIDGE_PROTOCOL_VERSION,
366
+ id,
367
+ error: data === void 0 ? { code, message } : { code, message, data }
368
+ };
369
+ postToIframe(response);
370
+ }
371
+ function sendResult(id, result) {
372
+ const response = {
373
+ type: "blink:rpc-response",
374
+ protocolVersion: BRIDGE_PROTOCOL_VERSION,
375
+ id,
376
+ result
377
+ };
378
+ postToIframe(response);
379
+ }
380
+ const onMessage = (event) => {
381
+ if (detached) return;
382
+ if (event.source !== iframeWindow) return;
383
+ if (event.origin !== iframeOrigin) return;
384
+ const message = parseBridgeMessage(event.data);
385
+ if (!message) return;
386
+ switch (message.type) {
387
+ case "blink:bridge-hello": {
388
+ advertiseWallets(discoverer.list());
389
+ discoverer.requestProviders();
390
+ return;
391
+ }
392
+ case "blink:rpc-request": {
393
+ const { id, uuid, method, params } = message;
394
+ console.info("[blink-bridge:parent] rpc-request", { uuid, method });
395
+ if (!ALLOWED_RPC_METHODS.has(method)) {
396
+ log("rejecting non-allowlisted method", { uuid, method });
397
+ sendError(id, -32601, `Method not allowed: ${method}`);
398
+ return;
399
+ }
400
+ const provider = discoverer.get(uuid);
401
+ if (!provider) {
402
+ log("no provider for uuid", { uuid, method });
403
+ sendError(id, -32602, `Unknown wallet: ${uuid}`);
404
+ return;
405
+ }
406
+ ensureSubscription(uuid, provider);
407
+ provider.request({ method, params }).then((result) => {
408
+ console.info("[blink-bridge:parent] rpc-result", { uuid, method, ok: true });
409
+ sendResult(id, result);
410
+ }).catch((err) => {
411
+ const { code, message: errMessage, data } = normalizeProviderError(err);
412
+ console.info("[blink-bridge:parent] rpc-error", { uuid, method, code, message: errMessage });
413
+ sendError(id, code, errMessage, data);
414
+ });
415
+ return;
416
+ }
417
+ case "blink:resolve-by-flag": {
418
+ const match = resolveByFlag(discoverer.list(), message.flags, log);
419
+ console.info("[blink-bridge:parent] resolveByFlag", {
420
+ flags: message.flags,
421
+ match: match ? { rdns: match.rdns, flag: match.flag } : null
422
+ });
423
+ const response = {
424
+ type: "blink:resolve-by-flag-response",
425
+ protocolVersion: BRIDGE_PROTOCOL_VERSION,
426
+ id: message.id,
427
+ match
428
+ };
429
+ postToIframe(response);
430
+ return;
431
+ }
432
+ case "blink:wallets-advertised":
433
+ case "blink:rpc-response":
434
+ case "blink:rpc-event":
435
+ case "blink:resolve-by-flag-response":
436
+ return;
437
+ }
438
+ };
439
+ window.addEventListener("message", onMessage);
440
+ return {
441
+ detach() {
442
+ if (detached) return;
443
+ detached = true;
444
+ window.removeEventListener("message", onMessage);
445
+ unsubscribeFromDiscoverer();
446
+ unsubscribeAll();
447
+ }
448
+ };
449
+ }
450
+ function noopLog() {
451
+ }
452
+ function resolveByFlag(snapshot, flags, log) {
453
+ for (const flag of flags) {
454
+ for (const entry of snapshot) {
455
+ let value;
456
+ try {
457
+ value = entry.provider[flag];
458
+ } catch (err) {
459
+ log("provider threw on flag read \u2014 skipping", {
460
+ rdns: entry.info.rdns,
461
+ flag,
462
+ error: err instanceof Error ? err.message : String(err)
463
+ });
464
+ continue;
465
+ }
466
+ if (value) {
467
+ return { rdns: entry.info.rdns, uuid: entry.info.uuid, flag };
468
+ }
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ function normalizeProviderError(err) {
474
+ if (err && typeof err === "object") {
475
+ const e = err;
476
+ const code = typeof e.code === "number" ? e.code : -32603;
477
+ const message = typeof e.message === "string" ? e.message : err instanceof Error ? err.message : "Provider error";
478
+ const data = "data" in e ? e.data : void 0;
479
+ return data === void 0 ? { code, message } : { code, message, data };
480
+ }
481
+ return { code: -32603, message: typeof err === "string" ? err : "Provider error" };
482
+ }
483
+
94
484
  // src/iframe.ts
95
485
  var STYLE_ID = "blink-deposit-styles";
96
486
  var CLOSE_DURATION_MS = 280;
@@ -126,13 +516,26 @@ function createIframe(url, containerElement) {
126
516
  const iframe = document.createElement("iframe");
127
517
  iframe.src = url;
128
518
  const iframeOrigin = new URL(url).origin;
129
- iframe.allow = `publickey-credentials-get ${iframeOrigin}; publickey-credentials-create ${iframeOrigin}`;
519
+ iframe.allow = `publickey-credentials-get ${iframeOrigin}; publickey-credentials-create ${iframeOrigin}; clipboard-write ${iframeOrigin}`;
130
520
  const handle = document.createElement("div");
131
521
  container.appendChild(handle);
132
522
  container.appendChild(iframe);
133
523
  overlay.appendChild(container);
134
524
  const mountTarget = containerElement ?? document.body;
135
525
  mountTarget.appendChild(overlay);
526
+ const discoverer = createWalletDiscoverer();
527
+ let rpcHostHandle = null;
528
+ const attachBridge = () => {
529
+ if (rpcHostHandle) return;
530
+ if (!iframe.contentWindow) return;
531
+ rpcHostHandle = attachRpcHost({
532
+ iframeWindow: iframe.contentWindow,
533
+ iframeOrigin,
534
+ discoverer
535
+ });
536
+ };
537
+ attachBridge();
538
+ iframe.addEventListener("load", attachBridge);
136
539
  const savedOverflow = document.body.style.overflow;
137
540
  document.body.style.overflow = "hidden";
138
541
  const onBackdropClick = (event) => {
@@ -159,6 +562,7 @@ function createIframe(url, containerElement) {
159
562
  if (closed) return;
160
563
  closed = true;
161
564
  removeListeners();
565
+ detachBridge();
162
566
  overlay.setAttribute("data-blink-closing", "");
163
567
  let removed = false;
164
568
  const removeOverlay = () => {
@@ -171,6 +575,14 @@ function createIframe(url, containerElement) {
171
575
  container.addEventListener("animationend", removeOverlay, { once: true });
172
576
  setTimeout(removeOverlay, CLOSE_DURATION_MS + 50);
173
577
  }
578
+ function detachBridge() {
579
+ iframe.removeEventListener("load", attachBridge);
580
+ if (rpcHostHandle) {
581
+ rpcHostHandle.detach();
582
+ rpcHostHandle = null;
583
+ }
584
+ discoverer.destroy();
585
+ }
174
586
  return {
175
587
  get contentWindow() {
176
588
  return iframe.contentWindow;
@@ -189,6 +601,7 @@ function createIframe(url, containerElement) {
189
601
  if (!closed) {
190
602
  closed = true;
191
603
  removeListeners();
604
+ detachBridge();
192
605
  overlay.remove();
193
606
  unlockScroll();
194
607
  }
@@ -311,8 +724,10 @@ function isValidTokenAddress(value) {
311
724
  return TOKEN_ADDRESS_RE.test(value) || SOLANA_ADDRESS_RE.test(value);
312
725
  }
313
726
  function validateDepositRequest(request) {
314
- if (!Number.isFinite(request.amount) || request.amount <= 0) {
315
- throw new DepositError("INVALID_REQUEST", "amount must be a positive number.");
727
+ if (request.amount !== null) {
728
+ if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
729
+ throw new DepositError("INVALID_REQUEST", "amount must be a positive number or null.");
730
+ }
316
731
  }
317
732
  if (!Number.isInteger(request.chainId) || request.chainId <= 0) {
318
733
  throw new DepositError("INVALID_REQUEST", "chainId must be a positive integer.");
@@ -627,12 +1042,17 @@ var Checkout = Deposit;
627
1042
  // src/index.ts
628
1043
  var VERSION = "0.3.0";
629
1044
 
1045
+ exports.ALLOWED_RPC_METHODS = ALLOWED_RPC_METHODS;
1046
+ exports.BRIDGE_PROTOCOL_VERSION = BRIDGE_PROTOCOL_VERSION;
630
1047
  exports.Checkout = Checkout;
631
1048
  exports.CheckoutError = CheckoutError;
632
1049
  exports.DEFAULT_WEBVIEW_BASE_URL = DEFAULT_WEBVIEW_BASE_URL;
633
1050
  exports.Deposit = Deposit;
634
1051
  exports.DepositError = DepositError;
635
1052
  exports.VERSION = VERSION;
1053
+ exports.attachRpcHost = attachRpcHost;
1054
+ exports.createWalletDiscoverer = createWalletDiscoverer;
636
1055
  exports.getDisplayMessage = getDisplayMessage;
1056
+ exports.parseBridgeMessage = parseBridgeMessage;
637
1057
  //# sourceMappingURL=index.cjs.map
638
1058
  //# sourceMappingURL=index.cjs.map