@fivenorth/loop-sdk 0.3.0 → 0.5.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 (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +61 -12
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -42,6 +42,10 @@ Before you can connect, you need to initialize the SDK. This is typically done o
42
42
  loop.init({
43
43
  appName: 'My Awesome dApp',
44
44
  network: 'local', // or 'devnet', 'mainnet'
45
+ options: {
46
+ openMode: 'popup', // 'popup' (default) or 'tab'
47
+ redirectUrl: 'https://myapp.com/after-connect', // optional redirect after approval
48
+ },
45
49
  onAccept: (provider) => {
46
50
  console.log('Connected!', provider);
47
51
  // You can now use the provider to interact with the wallet
@@ -55,6 +59,9 @@ loop.init({
55
59
  The `init` method takes a configuration object with the following properties:
56
60
  - `appName`: The name of your application, which will be displayed to the user in the Loop wallet.
57
61
  - `network`: The network to connect to. Can be `local`, `devnet`, or `mainnet`.
62
+ - `options`: Optional object containing:
63
+ - `openMode`: Controls how Loop opens: `'popup'` (default) or `'tab'`.
64
+ - `redirectUrl`: Optional redirect URL the wallet will navigate back to after successful approval. If omitted, user stays on Loop dashboard.
58
65
  - `onAccept`: A callback function that is called when the user accepts the connection. It receives a `provider` object.
59
66
  - `onReject`: A callback function that is called when the user rejects the connection.
60
67
 
package/dist/index.js CHANGED
@@ -2165,13 +2165,15 @@ class Connection {
2165
2165
  throw new Error("Session verification failed.");
2166
2166
  }
2167
2167
  const data = await response.json();
2168
+ const email = data?.email;
2168
2169
  if (!data?.party_id || !data?.public_key) {
2169
2170
  throw new Error("Invalid session verification response.");
2170
2171
  }
2171
2172
  const account = {
2172
2173
  party_id: data?.party_id,
2173
2174
  auth_token: authToken,
2174
- public_key: data?.public_key
2175
+ public_key: data?.public_key,
2176
+ email
2175
2177
  };
2176
2178
  return account;
2177
2179
  }
@@ -2205,8 +2207,25 @@ class RejectRequestError extends Error {
2205
2207
  }
2206
2208
 
2207
2209
  // src/provider.ts
2210
+ function generateUUID() {
2211
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
2212
+ const gCrypto = globalThis.crypto;
2213
+ if (!gCrypto?.getRandomValues) {
2214
+ const n2 = Number(c);
2215
+ return (n2 ^ Math.random() * 16 >> n2 / 4).toString(16);
2216
+ }
2217
+ const arr = gCrypto.getRandomValues(new Uint8Array(1));
2218
+ const byte = arr[0];
2219
+ const n = Number(c);
2220
+ return (n ^ (byte & 15) >> n / 4).toString(16);
2221
+ });
2222
+ }
2208
2223
  function generateRequestId() {
2209
- return crypto.randomUUID();
2224
+ const gCrypto = globalThis.crypto;
2225
+ if (gCrypto?.randomUUID) {
2226
+ return gCrypto.randomUUID();
2227
+ }
2228
+ return generateUUID();
2210
2229
  }
2211
2230
 
2212
2231
  class Provider {
@@ -2290,16 +2309,31 @@ class LoopSDK {
2290
2309
  provider = null;
2291
2310
  openMode = "popup";
2292
2311
  popupWindow = null;
2312
+ redirectUrl;
2293
2313
  onAccept = null;
2294
2314
  onReject = null;
2295
2315
  overlay = null;
2296
2316
  ticketId = null;
2297
2317
  constructor() {}
2298
- init({ appName, network, walletUrl, apiUrl, onAccept, onReject, openMode }) {
2318
+ init({
2319
+ appName,
2320
+ network,
2321
+ walletUrl,
2322
+ apiUrl,
2323
+ onAccept,
2324
+ onReject,
2325
+ options
2326
+ }) {
2299
2327
  this.appName = appName;
2300
2328
  this.onAccept = onAccept || null;
2301
2329
  this.onReject = onReject || null;
2302
- this.openMode = openMode ?? "popup";
2330
+ const resolvedOptions = {
2331
+ openMode: "popup",
2332
+ redirectUrl: undefined,
2333
+ ...options ?? {}
2334
+ };
2335
+ this.openMode = resolvedOptions.openMode;
2336
+ this.redirectUrl = resolvedOptions.redirectUrl;
2303
2337
  this.connection = new Connection({ network, walletUrl, apiUrl });
2304
2338
  }
2305
2339
  async connect() {
@@ -2338,7 +2372,12 @@ class LoopSDK {
2338
2372
  }
2339
2373
  if (ticketId && canReuseTicket) {
2340
2374
  this.ticketId = ticketId;
2341
- const connectUrl = `${this.connection.walletUrl}/.connect/?ticketId=${ticketId}`;
2375
+ const url = new URL("/.connect/", this.connection.walletUrl);
2376
+ url.searchParams.set("ticketId", ticketId);
2377
+ if (this.redirectUrl) {
2378
+ url.searchParams.set("redirectUrl", this.redirectUrl);
2379
+ }
2380
+ const connectUrl = url.toString();
2342
2381
  this.showQrCode(connectUrl);
2343
2382
  this.connection.connectWebSocket(ticketId, this.handleWebSocketMessage.bind(this));
2344
2383
  return;
@@ -2353,7 +2392,12 @@ class LoopSDK {
2353
2392
  const { ticket_id: ticketId } = await this.connection.getTicket(this.appName, sessionId, this.version);
2354
2393
  this.ticketId = ticketId;
2355
2394
  localStorage.setItem("loop_connect", JSON.stringify({ sessionId, ticketId }));
2356
- const connectUrl = `${this.connection.walletUrl}/.connect/?ticketId=${ticketId}`;
2395
+ const url = new URL("/.connect/", this.connection.walletUrl);
2396
+ url.searchParams.set("ticketId", ticketId);
2397
+ if (this.redirectUrl) {
2398
+ url.searchParams.set("redirectUrl", this.redirectUrl);
2399
+ }
2400
+ const connectUrl = url.toString();
2357
2401
  this.showQrCode(connectUrl);
2358
2402
  this.connection.connectWebSocket(ticketId, this.handleWebSocketMessage.bind(this));
2359
2403
  } catch (error) {
@@ -2382,9 +2426,6 @@ class LoopSDK {
2382
2426
  this.hideQrCode();
2383
2427
  this.connection?.connectWebSocket(connectionInfo.ticketId, this.handleWebSocketMessage.bind(this));
2384
2428
  console.log("[LoopSDK] HANDSHAKE_ACCEPT: closing popup (if exists)");
2385
- if (this.popupWindow && !this.popupWindow.closed) {
2386
- this.popupWindow.close();
2387
- }
2388
2429
  this.popupWindow = null;
2389
2430
  } catch (error) {
2390
2431
  console.error("Failed to update local storage with auth token.", error);
@@ -2439,20 +2480,27 @@ class LoopSDK {
2439
2480
  return;
2440
2481
  }
2441
2482
  const overlay = document.createElement("div");
2483
+ overlay.id = "loop-sdk-connect-overlay";
2484
+ overlay.className = "loop-sdk-connect-overlay";
2442
2485
  overlay.style.position = "fixed";
2443
2486
  overlay.style.top = "0";
2444
2487
  overlay.style.left = "0";
2445
2488
  overlay.style.width = "100%";
2446
2489
  overlay.style.height = "100%";
2447
- overlay.style.backgroundColor = "rgba(0,0,0,0.5)";
2490
+ overlay.style.backgroundColor = "rgba(0,0,0,0.9)";
2448
2491
  overlay.style.display = "flex";
2449
2492
  overlay.style.justifyContent = "center";
2450
2493
  overlay.style.alignItems = "center";
2451
2494
  overlay.style.zIndex = "1000";
2452
2495
  overlay.style.flexDirection = "column";
2496
+ const content = document.createElement("div");
2497
+ content.className = "loop-sdk-connect-content";
2498
+ content.style.display = "flex";
2499
+ content.style.flexDirection = "column";
2500
+ content.style.alignItems = "center";
2453
2501
  const img = document.createElement("img");
2454
2502
  img.src = dataUrl;
2455
- overlay.appendChild(img);
2503
+ content.appendChild(img);
2456
2504
  const link = document.createElement("a");
2457
2505
  link.href = url;
2458
2506
  link.textContent = "Or click here to connect";
@@ -2462,7 +2510,8 @@ class LoopSDK {
2462
2510
  e.preventDefault();
2463
2511
  this.openWallet(url);
2464
2512
  };
2465
- overlay.appendChild(link);
2513
+ content.appendChild(link);
2514
+ overlay.appendChild(content);
2466
2515
  overlay.onclick = (e) => {
2467
2516
  if (e.target === overlay) {
2468
2517
  this.hideQrCode();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fivenorth/loop-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "author": "hello@fivenorth.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -16,6 +16,7 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
+ "repository": "github:fivenorth-io/loop-sdk",
19
20
  "scripts": {
20
21
  "build": "bun build ./src/index.ts --outdir ./dist",
21
22
  "prepublishOnly": "bun run build",