@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.
- package/README.md +7 -0
- package/dist/index.js +61 -12
- 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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
"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",
|