@lukso/core 1.2.10 → 1.2.11-dev.6cff3c3

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 CHANGED
@@ -1,11 +1,31 @@
1
1
  # @lukso/core
2
2
 
3
- This is core package that contain low level elements that are used by LUKSO web components and applications.
3
+ [![npm version](https://img.shields.io/npm/v/@lukso/core.svg?style=flat)](https://www.npmjs.com/package/@lukso/core)
4
+
5
+ Core utilities, services, and mixins for LUKSO web components and applications.
4
6
 
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
10
  pnpm add @lukso/core
11
+ # or
12
+ npm install @lukso/core
13
+ # or
14
+ yarn add @lukso/core
15
+ ```
16
+
17
+ ### Peer Dependencies
18
+
19
+ This package requires `lit` as a peer dependency (for the Lit mixins):
20
+
21
+ ```bash
22
+ pnpm add lit
23
+ ```
24
+
25
+ If you're using chain definitions with viem types, you'll also need:
26
+
27
+ ```bash
28
+ pnpm add viem
9
29
  ```
10
30
 
11
31
  ## Features
@@ -116,6 +136,116 @@ The `slug` function is useful for:
116
136
  - Generating CSS class names
117
137
  - Normalizing user input
118
138
 
139
+ #### Signed QR Codes (Check-in URLs)
140
+
141
+ Create and verify signed QR codes for Universal Profile check-ins. This is useful for proving ownership of a Universal Profile without on-chain transactions.
142
+
143
+ **URI Format:** `ethereum:<address>@<chainId>?ts=<unixTimestamp>&sig=<signature>`
144
+
145
+ ```typescript
146
+ import {
147
+ createSignedQR,
148
+ verifySignedQR,
149
+ parseSignedQR,
150
+ isSignedQRFormat,
151
+ getControllerAddress,
152
+ getEIP1271Data,
153
+ EIP1271_MAGIC_VALUE,
154
+ } from '@lukso/core/utils'
155
+ ```
156
+
157
+ **Creating a signed QR code:**
158
+
159
+ ```typescript
160
+ // Using a private key directly
161
+ const uri = await createSignedQR(
162
+ '0x1234...', // controller private key
163
+ '0xabcd...', // profile address
164
+ 42, // LUKSO mainnet (or 4201 for testnet)
165
+ { generationOffsetSeconds: 2 } // optional: account for QR display latency
166
+ )
167
+ // Returns: ethereum:0xabcd...@42?ts=1706345678&sig=0x...
168
+
169
+ // Using a custom signer (WalletConnect, hardware wallet, etc.)
170
+ const uri = await createSignedQR(
171
+ null, // not needed when signer is provided
172
+ '0xabcd...', // profile address
173
+ 42,
174
+ {
175
+ signer: async (message) => await wallet.signMessage(message)
176
+ }
177
+ )
178
+ ```
179
+
180
+ **Verifying a signed QR code:**
181
+
182
+ ```typescript
183
+ const result = await verifySignedQR(uri, {
184
+ maxAgeSeconds: 60, // default: 60 seconds
185
+ })
186
+
187
+ if (result.isValid) {
188
+ console.log('Profile:', result.profileAddress)
189
+ console.log('Chain ID:', result.chainId)
190
+ console.log('Signed by:', result.recoveredAddress)
191
+ console.log('Timestamp:', result.timestamp)
192
+ console.log('Is expired:', result.isExpired)
193
+ }
194
+ ```
195
+
196
+ **EIP-1271 verification (for smart contract wallets):**
197
+
198
+ ```typescript
199
+ import { createPublicClient, http } from 'viem'
200
+ import { lukso } from 'viem/chains'
201
+
202
+ const result = await verifySignedQR(uri)
203
+ const { hash, signature } = getEIP1271Data(uri, result)
204
+
205
+ // Call isValidSignature on the Universal Profile contract
206
+ const client = createPublicClient({ chain: lukso, transport: http() })
207
+ const magicValue = await client.readContract({
208
+ address: result.profileAddress,
209
+ abi: [{
210
+ name: 'isValidSignature',
211
+ type: 'function',
212
+ stateMutability: 'view',
213
+ inputs: [
214
+ { name: 'dataHash', type: 'bytes32' },
215
+ { name: 'signature', type: 'bytes' },
216
+ ],
217
+ outputs: [{ name: '', type: 'bytes4' }],
218
+ }],
219
+ functionName: 'isValidSignature',
220
+ args: [hash, signature],
221
+ })
222
+
223
+ const isAuthorizedController = magicValue === EIP1271_MAGIC_VALUE
224
+ ```
225
+
226
+ **Quick format check:**
227
+
228
+ ```typescript
229
+ if (isSignedQRFormat(uri)) {
230
+ // Valid format, proceed with verification
231
+ const parsed = parseSignedQR(uri)
232
+ console.log(parsed?.profileAddress, parsed?.chainId)
233
+ }
234
+ ```
235
+
236
+ **Get controller address from private key:**
237
+
238
+ ```typescript
239
+ const controllerAddress = getControllerAddress('0x1234...')
240
+ console.log('Will sign as:', controllerAddress)
241
+ ```
242
+
243
+ The signed QR code functionality is useful for:
244
+ - Event check-ins without on-chain transactions
245
+ - Proving Universal Profile ownership
246
+ - Time-limited access tokens
247
+ - QR-based authentication flows
248
+
119
249
  ### Lit Mixins
120
250
 
121
251
  #### withDeviceService
@@ -233,18 +363,56 @@ import type { ChainExtended, NetworkSlug, Address } from '@lukso/core'
233
363
 
234
364
  ### Configurations
235
365
 
236
- Package provide some of basic constants.
237
-
366
+ Package provides some basic constants.
238
367
 
239
368
  ```typescript
240
369
  import { SUPPORTED_NETWORK_IDS, GRAPHQL_ENDPOINT_MAINNET, GRAPHQL_ENDPOINT_TESTNET } from '@lukso/core/config'
241
370
  ```
242
371
 
372
+ ## Using Without a Bundler (Import Maps)
243
373
 
374
+ If you need to use `@lukso/core` in environments without a bundler (e.g., plain HTML, WordPress, static sites), you can use import maps to resolve bare module specifiers.
244
375
 
376
+ Since this package uses Lit as a peer dependency and marks it as external, it outputs bare imports like `import { html } from 'lit'`. Browsers can't resolve these without help.
245
377
 
378
+ ### Example: Using Import Maps
379
+
380
+ ```html
381
+ <!DOCTYPE html>
382
+ <html>
383
+ <head>
384
+ <script type="importmap">
385
+ {
386
+ "imports": {
387
+ "lit": "https://cdn.jsdelivr.net/npm/lit@3.3/+esm",
388
+ "lit/": "https://cdn.jsdelivr.net/npm/lit@3.3/",
389
+ "@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2/+esm",
390
+ "@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2/",
391
+ "@preact/signals-core": "https://cdn.jsdelivr.net/npm/@preact/signals-core@1/+esm",
392
+ "@lukso/core": "https://cdn.jsdelivr.net/npm/@lukso/core/+esm",
393
+ "@lukso/core/": "https://cdn.jsdelivr.net/npm/@lukso/core/"
394
+ }
395
+ }
396
+ </script>
397
+ </head>
398
+ <body>
399
+ <script type="module">
400
+ import { deviceService } from '@lukso/core/services/device'
401
+ import { luksoMainnet } from '@lukso/core/chains'
402
+
403
+ const device = deviceService()
404
+ console.log('Is mobile:', device.isMobile)
405
+ console.log('Chain ID:', luksoMainnet.id)
406
+ </script>
407
+ </body>
408
+ </html>
409
+ ```
246
410
 
411
+ ### Notes on Import Maps
247
412
 
413
+ - **Browser Support**: Import maps are supported in all modern browsers (Chrome 89+, Safari 16.4+, Firefox 108+)
414
+ - **Must be before scripts**: The `<script type="importmap">` must appear before any `<script type="module">`
415
+ - **Trailing slashes matter**: Use `"@lukso/core/"` to map subpath imports like `@lukso/core/services`
248
416
 
249
417
  ## License
250
418
 
@@ -0,0 +1,260 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); 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; }// src/utils/browserInfo.ts
2
+ var EXTENSION_STORE_LINKS = {
3
+ chrome: "https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn",
4
+ brave: "https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn",
5
+ edge: "https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn",
6
+ opera: "",
7
+ safari: "",
8
+ firefox: ""
9
+ };
10
+ var browserInfo = (deviceService) => {
11
+ const browserInfoDefaults = {
12
+ id: "chrome",
13
+ name: "",
14
+ icon: ""
15
+ };
16
+ const detectBrowser = () => {
17
+ const { isChrome, isBrave, isFirefox, isSafari, isEdge, isOpera } = deviceService;
18
+ if (isBrave) {
19
+ return {
20
+ id: "brave",
21
+ name: "Brave",
22
+ icon: "logo-brave",
23
+ storeLink: EXTENSION_STORE_LINKS.brave
24
+ };
25
+ }
26
+ if (isEdge) {
27
+ return {
28
+ id: "edge",
29
+ name: "Edge",
30
+ icon: "logo-edge",
31
+ storeLink: EXTENSION_STORE_LINKS.edge
32
+ };
33
+ }
34
+ if (isOpera) {
35
+ return {
36
+ id: "opera",
37
+ name: "Opera",
38
+ icon: "logo-opera",
39
+ storeLink: EXTENSION_STORE_LINKS.opera
40
+ };
41
+ }
42
+ if (isChrome) {
43
+ return {
44
+ id: "chrome",
45
+ name: "Chrome",
46
+ icon: "logo-chrome",
47
+ storeLink: EXTENSION_STORE_LINKS.chrome
48
+ };
49
+ }
50
+ if (isFirefox) {
51
+ return {
52
+ id: "firefox",
53
+ name: "Firefox",
54
+ icon: "logo-firefox",
55
+ storeLink: EXTENSION_STORE_LINKS.firefox
56
+ };
57
+ }
58
+ if (isSafari) {
59
+ return {
60
+ id: "safari",
61
+ name: "Safari",
62
+ icon: "logo-safari",
63
+ storeLink: EXTENSION_STORE_LINKS.safari
64
+ };
65
+ }
66
+ };
67
+ const browserInfo2 = { ...browserInfoDefaults, ...detectBrowser() };
68
+ return browserInfo2;
69
+ };
70
+
71
+ // src/utils/signed-profile-urls.ts
72
+ var _viem = require('viem');
73
+ var _accounts = require('viem/accounts');
74
+ var URI_SCHEME = "ethereum";
75
+ var URI_PATTERN = /^ethereum:(0x[a-fA-F0-9]{40})@(\d+)\?ts=(\d+)&sig=(0x[a-fA-F0-9]+)$/;
76
+ function createMessage(profileAddress, chainId, timestamp) {
77
+ return `${URI_SCHEME}:${profileAddress.toLowerCase()}@${chainId}?ts=${timestamp}`;
78
+ }
79
+ var MAX_GENERATION_OFFSET_SECONDS = 5;
80
+ async function createSignedQR(privateKey, profileAddress, chainId, options) {
81
+ const requestedOffset = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.generationOffsetSeconds]), () => ( 0));
82
+ const generationOffset = Math.min(
83
+ Math.max(0, requestedOffset),
84
+ MAX_GENERATION_OFFSET_SECONDS
85
+ );
86
+ const timestamp = Math.floor(Date.now() / 1e3) + generationOffset;
87
+ const message = createMessage(profileAddress, chainId, timestamp);
88
+ let signature;
89
+ if (_optionalChain([options, 'optionalAccess', _2 => _2.signer])) {
90
+ signature = await options.signer(message);
91
+ } else if (privateKey && privateKey !== "0x") {
92
+ const account = _accounts.privateKeyToAccount.call(void 0, privateKey);
93
+ signature = await account.signMessage({ message });
94
+ } else {
95
+ throw new Error(
96
+ "Either a valid privateKey or options.signer must be provided"
97
+ );
98
+ }
99
+ return `${message}&sig=${signature}`;
100
+ }
101
+ function parseSignedQR(uri) {
102
+ const match = uri.match(URI_PATTERN);
103
+ if (!match) {
104
+ return null;
105
+ }
106
+ const [, profileAddress, chainIdStr, timestampStr, signature] = match;
107
+ return {
108
+ profileAddress: profileAddress.toLowerCase(),
109
+ chainId: Number.parseInt(chainIdStr, 10),
110
+ timestamp: Number.parseInt(timestampStr, 10),
111
+ signature,
112
+ message: createMessage(
113
+ profileAddress,
114
+ Number.parseInt(chainIdStr, 10),
115
+ Number.parseInt(timestampStr, 10)
116
+ )
117
+ };
118
+ }
119
+ async function verifySignedQR(uri, options) {
120
+ const maxAgeSeconds = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _3 => _3.maxAgeSeconds]), () => ( 60));
121
+ const skipTimestampValidation = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.skipTimestampValidation]), () => ( false));
122
+ const parsed = parseSignedQR(uri);
123
+ if (!parsed) {
124
+ throw new Error("Invalid QR format: does not match expected URI pattern");
125
+ }
126
+ const { profileAddress, chainId, timestamp, signature, message } = parsed;
127
+ const messageHash = _viem.hashMessage.call(void 0, message);
128
+ const recoveredAddress = await _viem.recoverMessageAddress.call(void 0, {
129
+ message,
130
+ signature
131
+ });
132
+ const now = Math.floor(Date.now() / 1e3);
133
+ const isExpired = !skipTimestampValidation && timestamp + maxAgeSeconds < now;
134
+ return {
135
+ isValid: !isExpired,
136
+ profileAddress,
137
+ chainId,
138
+ timestamp,
139
+ recoveredAddress: recoveredAddress.toLowerCase(),
140
+ isExpired,
141
+ message,
142
+ messageHash
143
+ };
144
+ }
145
+ function isSignedQRFormat(uri) {
146
+ return URI_PATTERN.test(uri);
147
+ }
148
+ function getControllerAddress(privateKey) {
149
+ const account = _accounts.privateKeyToAccount.call(void 0, privateKey);
150
+ return account.address;
151
+ }
152
+ var EIP1271_MAGIC_VALUE = "0x1626ba7e";
153
+ function getEIP1271Data(uri, verificationResult) {
154
+ const parsed = parseSignedQR(uri);
155
+ if (!parsed) {
156
+ throw new Error("Invalid QR format");
157
+ }
158
+ return {
159
+ hash: verificationResult.messageHash,
160
+ signature: parsed.signature
161
+ };
162
+ }
163
+
164
+ // src/utils/slug.ts
165
+ var slug = (value) => {
166
+ if (!value) {
167
+ return "";
168
+ }
169
+ return value.toLowerCase().replace(/\s+/g, "-");
170
+ };
171
+
172
+ // src/utils/url-resolver.ts
173
+ var UrlConverter = class {
174
+ /**
175
+ * It will relatively append pathname or hostname to the destination URL
176
+ *
177
+ * For example:
178
+ * destination=https://some.api.gateway/something/ipfs
179
+ * url=ipfs://QmSomeHash
180
+ * output=https://some.api.gateway/something/ipfs/QmSomeHash
181
+ *
182
+ * destination=https://some.api.gateway/something/ipfs
183
+ * url=https://something.com/somewhere
184
+ * output=https://some.api.gateway/something/ipfs/somewhere
185
+ *
186
+ * @param destination destination string | URL
187
+ */
188
+ constructor(destination) {
189
+ this.destination = new URL(destination);
190
+ if (this.destination.pathname.at(-1) !== "/") {
191
+ this.destination.pathname += "/";
192
+ }
193
+ }
194
+ resolveUrl(url) {
195
+ const source = new URL(url);
196
+ const relativePath = source.pathname ? `./${source.hostname}${source.pathname}` : `./${source.hostname}`;
197
+ const out = new URL(relativePath, this.destination);
198
+ out.pathname = out.pathname.replaceAll(/\/\/+/g, "/");
199
+ return out.toString();
200
+ }
201
+ };
202
+ var UrlResolver = class {
203
+ constructor(converters) {
204
+ this.converters = [];
205
+ for (const item of converters) {
206
+ const [match, _converter] = item;
207
+ if (match == null) {
208
+ throw new TypeError("Match criteria not defined");
209
+ }
210
+ const converter = typeof _converter === "string" ? new UrlConverter(_converter) : _converter;
211
+ if (!(converter instanceof UrlConverter)) {
212
+ throw new TypeError("Invalid converter");
213
+ }
214
+ this.converters.push({ match, converter });
215
+ }
216
+ }
217
+ /**
218
+ * Resolves a URL to a gateway URL.
219
+ * Supports possible multiple converters transforming the URL
220
+ * in sequence until no converter matches.
221
+ *
222
+ * @param {string} url to resolve
223
+ * @returns {string} resolved url (if resolver is found, otherwise the parameter url is returned)
224
+ */
225
+ resolveUrl(url_) {
226
+ let url = url_;
227
+ const current = new Set(this.converters);
228
+ let found = true;
229
+ while (found) {
230
+ found = false;
231
+ for (const entry of current) {
232
+ const { match, converter } = entry;
233
+ if (match instanceof RegExp ? match.test(url) : url.startsWith(match)) {
234
+ url = converter.resolveUrl(url);
235
+ current.delete(entry);
236
+ found = true;
237
+ break;
238
+ }
239
+ }
240
+ }
241
+ return url;
242
+ }
243
+ };
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+ exports.EXTENSION_STORE_LINKS = EXTENSION_STORE_LINKS; exports.browserInfo = browserInfo; exports.createMessage = createMessage; exports.createSignedQR = createSignedQR; exports.parseSignedQR = parseSignedQR; exports.verifySignedQR = verifySignedQR; exports.isSignedQRFormat = isSignedQRFormat; exports.getControllerAddress = getControllerAddress; exports.EIP1271_MAGIC_VALUE = EIP1271_MAGIC_VALUE; exports.getEIP1271Data = getEIP1271Data; exports.slug = slug; exports.UrlConverter = UrlConverter; exports.UrlResolver = UrlResolver;
260
+ //# sourceMappingURL=chunk-2KVLFGLM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/service-auth-simple/service-auth-simple/packages/core/dist/chunk-2KVLFGLM.cjs","../src/utils/browserInfo.ts","../src/utils/signed-profile-urls.ts","../src/utils/slug.ts","../src/utils/url-resolver.ts"],"names":["browserInfo"],"mappings":"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","file":"/home/runner/work/service-auth-simple/service-auth-simple/packages/core/dist/chunk-2KVLFGLM.cjs","sourcesContent":[null,"import type { DeviceService } from '../services'\n\nexport type BrowserName =\n | 'chrome'\n | 'safari'\n | 'firefox'\n | 'edge'\n | 'opera'\n | 'brave'\n\nexport type BrowserInfo = {\n id: BrowserName\n name: string\n icon: string\n storeLink: string\n}\n\n// extension store links (all webkit based browsers use chrome web store installation)\nexport const EXTENSION_STORE_LINKS = {\n chrome:\n 'https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn',\n brave:\n 'https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn',\n edge: 'https://chrome.google.com/webstore/detail/universal-profiles-testin/abpickdkkbnbcoepogfhkhennhfhehfn',\n opera: '',\n safari: '',\n firefox: '',\n}\n\n/**\n * Expose browser info to the app\n */\nexport const browserInfo = (deviceService: DeviceService): BrowserInfo => {\n const browserInfoDefaults = {\n id: 'chrome',\n name: '',\n icon: '',\n } as BrowserInfo\n\n const detectBrowser = (): BrowserInfo | undefined => {\n const { isChrome, isBrave, isFirefox, isSafari, isEdge, isOpera } =\n deviceService\n\n if (isBrave) {\n return {\n id: 'brave',\n name: 'Brave',\n icon: 'logo-brave',\n storeLink: EXTENSION_STORE_LINKS.brave,\n }\n }\n\n if (isEdge) {\n return {\n id: 'edge',\n name: 'Edge',\n icon: 'logo-edge',\n storeLink: EXTENSION_STORE_LINKS.edge,\n }\n }\n\n if (isOpera) {\n return {\n id: 'opera',\n name: 'Opera',\n icon: 'logo-opera',\n storeLink: EXTENSION_STORE_LINKS.opera,\n }\n }\n\n if (isChrome) {\n return {\n id: 'chrome',\n name: 'Chrome',\n icon: 'logo-chrome',\n storeLink: EXTENSION_STORE_LINKS.chrome,\n }\n }\n\n if (isFirefox) {\n return {\n id: 'firefox',\n name: 'Firefox',\n icon: 'logo-firefox',\n storeLink: EXTENSION_STORE_LINKS.firefox,\n }\n }\n\n if (isSafari) {\n return {\n id: 'safari',\n name: 'Safari',\n icon: 'logo-safari',\n storeLink: EXTENSION_STORE_LINKS.safari,\n }\n }\n }\n\n const browserInfo = { ...browserInfoDefaults, ...detectBrowser() }\n\n return browserInfo\n}\n","/**\n * @service-checkin/signed-qr-code\n *\n * Create and verify signed QR codes for Universal Profile check-ins.\n * Pure TypeScript - compatible with React Native, Node.js, and browsers.\n *\n * URI Format: ethereum:<address>@<chainId>?ts=<unixTimestamp>&sig=<signature>\n *\n * The signed message is everything before &sig= (the URI without the signature).\n * Uses EIP-191 prefixed signing (signMessage) for safety - this prevents\n * signed messages from being confused with transaction hashes.\n */\n\nimport { type Hex, hashMessage, recoverMessageAddress } from 'viem'\nimport { privateKeyToAccount } from 'viem/accounts'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SignedQRData {\n /** The Universal Profile address */\n profileAddress: string\n /** The chain ID (42 for LUKSO mainnet, 4201 for testnet) */\n chainId: number\n /** Unix timestamp (seconds since epoch) */\n timestamp: number\n /** The signature over the message */\n signature: Hex\n}\n\n/**\n * Custom signer function type.\n * Takes the message string and returns an EIP-191 signature.\n * The signer should use `signMessage` (which adds the EIP-191 prefix).\n * Compatible with hardware wallets, secure enclaves, WalletConnect, etc.\n *\n * The EIP-191 prefix prevents signed messages from being confused with\n * transaction hashes, making this safe for identity verification.\n */\nexport type SignerFunction = (message: string) => Promise<Hex>\n\nexport interface CreateSignedQROptions {\n /**\n * Seconds to add to current time to account for QR generation/display latency.\n * The QR timestamp will be set to (now + generationOffsetSeconds).\n * Capped at 5 seconds maximum to prevent abuse.\n * Default: 0\n */\n generationOffsetSeconds?: number\n\n /**\n * Custom signer function for signing the message.\n * If provided, the privateKey parameter can be omitted or set to null.\n * Useful for hardware wallets, secure enclaves, or WalletConnect.\n *\n * The signer receives the message string and should use `signMessage`\n * (EIP-191 prefixed signing) to return the signature.\n *\n * @example\n * ```ts\n * // Using a wallet that signs messages\n * const uri = await createSignedQR(null, profileAddress, 42, {\n * signer: async (message) => {\n * return await wallet.signMessage(message)\n * }\n * })\n * ```\n */\n signer?: SignerFunction\n}\n\nexport interface VerifySignedQROptions {\n /** Maximum age in seconds before QR is considered expired (default: 60) */\n maxAgeSeconds?: number\n /** Skip timestamp validation (useful for testing) */\n skipTimestampValidation?: boolean\n}\n\nexport interface VerificationResult {\n /** Whether the QR code is valid and not expired */\n isValid: boolean\n /** The Universal Profile address from the QR */\n profileAddress: string\n /** The chain ID from the QR */\n chainId: number\n /** The timestamp from the QR (unix seconds) */\n timestamp: number\n /** The address recovered from the signature (the controller) */\n recoveredAddress: string\n /** Whether the QR has expired based on maxAgeSeconds */\n isExpired: boolean\n /** The original signed message (for EIP-1271 verification) */\n message: string\n /** The hash of the message (for EIP-1271 verification) */\n messageHash: Hex\n}\n\nexport interface ParsedQRData {\n profileAddress: string\n chainId: number\n timestamp: number\n signature: Hex\n message: string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** URI scheme for signed QR codes */\nexport const URI_SCHEME = 'ethereum'\n\n/** Regex pattern for parsing signed QR URIs */\nconst URI_PATTERN =\n /^ethereum:(0x[a-fA-F0-9]{40})@(\\d+)\\?ts=(\\d+)&sig=(0x[a-fA-F0-9]+)$/\n\n// ============================================================================\n// Core Functions\n// ============================================================================\n\n/**\n * Create the message that will be signed.\n * This is the URI without the signature parameter.\n */\nexport function createMessage(\n profileAddress: string,\n chainId: number,\n timestamp: number\n): string {\n return `${URI_SCHEME}:${profileAddress.toLowerCase()}@${chainId}?ts=${timestamp}`\n}\n\n/** Maximum allowed generation offset to prevent abuse */\nconst MAX_GENERATION_OFFSET_SECONDS = 5\n\n/**\n * Create a signed QR URI string.\n *\n * @param privateKey - The controller's private key (hex string with 0x prefix).\n * Can be '0x' or omitted if using options.signer.\n * @param profileAddress - The Universal Profile address to sign for\n * @param chainId - The chain ID (42 for LUKSO mainnet, 4201 for testnet)\n * @param options - Optional configuration including custom signer\n * @returns The complete signed URI string ready to encode as QR\n *\n * @example\n * ```ts\n * // Using a private key directly\n * const uri = await createSignedQR(\n * '0x1234...', // controller private key\n * '0xabcd...', // profile address\n * 42, // LUKSO mainnet\n * { generationOffsetSeconds: 2 }\n * )\n *\n * // Using a custom signer (WalletConnect, hardware wallet, etc.)\n * const uri = await createSignedQR(\n * null, // not needed when signer is provided\n * '0xabcd...', // profile address\n * 42, // LUKSO mainnet\n * {\n * signer: async (message) => await wallet.signMessage(message)\n * }\n * )\n * // Returns: ethereum:0xabcd...@42?ts=1706345678&sig=0x...\n * ```\n */\nexport async function createSignedQR(\n privateKey: Hex | null,\n profileAddress: string,\n chainId: number,\n options?: CreateSignedQROptions\n): Promise<string> {\n // Cap the offset at MAX_GENERATION_OFFSET_SECONDS to prevent abuse\n const requestedOffset = options?.generationOffsetSeconds ?? 0\n const generationOffset = Math.min(\n Math.max(0, requestedOffset),\n MAX_GENERATION_OFFSET_SECONDS\n )\n const timestamp = Math.floor(Date.now() / 1000) + generationOffset\n\n const message = createMessage(profileAddress, chainId, timestamp)\n\n let signature: Hex\n\n if (options?.signer) {\n // Use custom signer (hardware wallet, secure enclave, WalletConnect, etc.)\n // Pass the message string - signer should use signMessage (EIP-191)\n signature = await options.signer(message)\n } else if (privateKey && privateKey !== '0x') {\n // Use private key directly with EIP-191 prefix (signMessage)\n const account = privateKeyToAccount(privateKey)\n signature = await account.signMessage({ message })\n } else {\n throw new Error(\n 'Either a valid privateKey or options.signer must be provided'\n )\n }\n\n return `${message}&sig=${signature}`\n}\n\n/**\n * Parse a signed QR URI without verification.\n * Use this when you need to extract data before full verification.\n *\n * @param uri - The signed QR URI string\n * @returns Parsed data or null if format is invalid\n */\nexport function parseSignedQR(uri: string): ParsedQRData | null {\n const match = uri.match(URI_PATTERN)\n\n if (!match) {\n return null\n }\n\n const [, profileAddress, chainIdStr, timestampStr, signature] = match\n\n return {\n profileAddress: profileAddress.toLowerCase(),\n chainId: Number.parseInt(chainIdStr, 10),\n timestamp: Number.parseInt(timestampStr, 10),\n signature: signature as Hex,\n message: createMessage(\n profileAddress,\n Number.parseInt(chainIdStr, 10),\n Number.parseInt(timestampStr, 10)\n ),\n }\n}\n\n/**\n * Verify a signed QR URI and recover the signer.\n *\n * This performs:\n * 1. URI format validation\n * 2. Signature recovery (gets the controller address that signed)\n * 3. Timestamp validation (checks if expired)\n *\n * Note: This does NOT verify that the recovered address is an authorized\n * controller of the profile. That check should be done separately using\n * EIP-1271 isValidSignature() or by checking controller permissions on-chain.\n *\n * @param uri - The signed QR URI string\n * @param options - Optional configuration\n * @returns Verification result with recovered address\n *\n * @example\n * ```ts\n * const result = await verifySignedQR(uri)\n *\n * if (result.isValid) {\n * console.log('Profile:', result.profileAddress)\n * console.log('Signed by:', result.recoveredAddress)\n *\n * // Now verify the signer is an authorized controller\n * // Option 1: Check if recoveredAddress === profileAddress (EOA case)\n * // Option 2: Call isValidSignature() on the profile contract\n * }\n * ```\n */\nexport async function verifySignedQR(\n uri: string,\n options?: VerifySignedQROptions\n): Promise<VerificationResult> {\n const maxAgeSeconds = options?.maxAgeSeconds ?? 60\n const skipTimestampValidation = options?.skipTimestampValidation ?? false\n\n // Parse the URI\n const parsed = parseSignedQR(uri)\n\n if (!parsed) {\n throw new Error('Invalid QR format: does not match expected URI pattern')\n }\n\n const { profileAddress, chainId, timestamp, signature, message } = parsed\n\n // Compute the EIP-191 message hash for storage\n const messageHash = hashMessage(message)\n\n // Recover the signer address using EIP-191 message recovery\n const recoveredAddress = await recoverMessageAddress({\n message,\n signature,\n })\n\n // Check if expired\n const now = Math.floor(Date.now() / 1000)\n const isExpired = !skipTimestampValidation && timestamp + maxAgeSeconds < now\n\n return {\n isValid: !isExpired,\n profileAddress,\n chainId,\n timestamp,\n recoveredAddress: recoveredAddress.toLowerCase(),\n isExpired,\n message,\n messageHash,\n }\n}\n\n/**\n * Check if a string looks like a valid signed QR URI.\n * This is a quick format check without full verification.\n */\nexport function isSignedQRFormat(uri: string): boolean {\n return URI_PATTERN.test(uri)\n}\n\n/**\n * Get the controller address from a private key.\n * Useful for displaying which address will sign the QR codes.\n */\nexport function getControllerAddress(privateKey: Hex): string {\n const account = privateKeyToAccount(privateKey)\n return account.address\n}\n\n// ============================================================================\n// EIP-1271 Helpers\n// ============================================================================\n\n/**\n * EIP-1271 magic value returned for valid signatures\n */\nexport const EIP1271_MAGIC_VALUE = '0x1626ba7e' as const\n\n/**\n * ABI for EIP-1271 isValidSignature function.\n * Use with viem's readContract to verify signatures on Universal Profiles.\n */\nexport const EIP1271_ABI = [\n {\n name: 'isValidSignature',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'dataHash', type: 'bytes32' },\n { name: 'signature', type: 'bytes' },\n ],\n outputs: [{ name: '', type: 'bytes4' }],\n },\n] as const\n\n/**\n * Prepare data for EIP-1271 verification.\n * Returns the hash and signature needed to call isValidSignature().\n *\n * @param uri - The original signed QR URI (needed to extract the signature)\n * @param verificationResult - The result from verifySignedQR()\n *\n * @example\n * ```ts\n * const result = await verifySignedQR(uri)\n * const { hash, signature } = getEIP1271Data(uri, result)\n *\n * const magicValue = await client.readContract({\n * address: result.profileAddress,\n * abi: EIP1271_ABI,\n * functionName: 'isValidSignature',\n * args: [hash, signature],\n * })\n *\n * const isAuthorizedController = magicValue === EIP1271_MAGIC_VALUE\n * ```\n */\nexport function getEIP1271Data(\n uri: string,\n verificationResult: VerificationResult\n): {\n hash: Hex\n signature: Hex\n} {\n const parsed = parseSignedQR(uri)\n if (!parsed) {\n throw new Error('Invalid QR format')\n }\n\n return {\n hash: verificationResult.messageHash,\n signature: parsed.signature,\n }\n}\n","/**\n * Make slug from text\n *\n * @param value\n * @returns\n */\nexport const slug = (value?: string) => {\n if (!value) {\n return ''\n }\n\n return value.toLowerCase().replace(/\\s+/g, '-') // convert spaces to hyphens\n}\n","export class UrlConverter {\n private destination: URL\n /**\n * It will relatively append pathname or hostname to the destination URL\n *\n * For example:\n * destination=https://some.api.gateway/something/ipfs\n * url=ipfs://QmSomeHash\n * output=https://some.api.gateway/something/ipfs/QmSomeHash\n *\n * destination=https://some.api.gateway/something/ipfs\n * url=https://something.com/somewhere\n * output=https://some.api.gateway/something/ipfs/somewhere\n *\n * @param destination destination string | URL\n */\n constructor(destination: string | URL) {\n this.destination = new URL(destination)\n if (this.destination.pathname.at(-1) !== '/') {\n this.destination.pathname += '/'\n }\n }\n\n resolveUrl(url: string): string {\n // Parse and convert to javascript URL objects\n // this will manage / and relative paths for us.\n const source = new URL(url)\n // extract the relative path. For URLs with a pathname prepend \".\" to make it ./ (i.e. relative)\n // for anything that only has a hostname we prepend ./ to make it relative\n // the pathname is at least slash for https urls, but '' for ipfs for example\n const relativePath = source.pathname\n ? `./${source.hostname}${source.pathname}` // pathname always starts with at least a slash\n : `./${source.hostname}`\n // Construct relative URL on destination using the relative pathname.\n const out = new URL(relativePath, this.destination)\n out.pathname = out.pathname.replaceAll(/\\/\\/+/g, '/')\n return out.toString()\n }\n}\n\nexport class UrlResolver {\n private converters: Array<{\n match: string | RegExp\n converter: UrlConverter\n }> = []\n constructor(converters: Array<[string | RegExp, UrlConverter | string]>) {\n for (const item of converters) {\n const [match, _converter] = item\n if (match == null) {\n throw new TypeError('Match criteria not defined')\n }\n const converter =\n typeof _converter === 'string'\n ? new UrlConverter(_converter)\n : _converter\n if (!(converter instanceof UrlConverter)) {\n throw new TypeError('Invalid converter')\n }\n this.converters.push({ match, converter })\n }\n }\n\n /**\n * Resolves a URL to a gateway URL.\n * Supports possible multiple converters transforming the URL\n * in sequence until no converter matches.\n *\n * @param {string} url to resolve\n * @returns {string} resolved url (if resolver is found, otherwise the parameter url is returned)\n */\n resolveUrl(url_: string): string {\n let url = url_\n const current = new Set<{\n match: string | RegExp\n converter: UrlConverter\n }>(this.converters)\n let found = true\n while (found) {\n found = false\n for (const entry of current) {\n const { match, converter } = entry\n if (match instanceof RegExp ? match.test(url) : url.startsWith(match)) {\n url = converter.resolveUrl(url)\n // This converter matches, so don't use it again.\n current.delete(entry)\n found = true\n break\n }\n }\n }\n return url\n }\n}\n"]}
@@ -68,6 +68,99 @@ var browserInfo = (deviceService) => {
68
68
  return browserInfo2;
69
69
  };
70
70
 
71
+ // src/utils/signed-profile-urls.ts
72
+ import { hashMessage, recoverMessageAddress } from "viem";
73
+ import { privateKeyToAccount } from "viem/accounts";
74
+ var URI_SCHEME = "ethereum";
75
+ var URI_PATTERN = /^ethereum:(0x[a-fA-F0-9]{40})@(\d+)\?ts=(\d+)&sig=(0x[a-fA-F0-9]+)$/;
76
+ function createMessage(profileAddress, chainId, timestamp) {
77
+ return `${URI_SCHEME}:${profileAddress.toLowerCase()}@${chainId}?ts=${timestamp}`;
78
+ }
79
+ var MAX_GENERATION_OFFSET_SECONDS = 5;
80
+ async function createSignedQR(privateKey, profileAddress, chainId, options) {
81
+ const requestedOffset = options?.generationOffsetSeconds ?? 0;
82
+ const generationOffset = Math.min(
83
+ Math.max(0, requestedOffset),
84
+ MAX_GENERATION_OFFSET_SECONDS
85
+ );
86
+ const timestamp = Math.floor(Date.now() / 1e3) + generationOffset;
87
+ const message = createMessage(profileAddress, chainId, timestamp);
88
+ let signature;
89
+ if (options?.signer) {
90
+ signature = await options.signer(message);
91
+ } else if (privateKey && privateKey !== "0x") {
92
+ const account = privateKeyToAccount(privateKey);
93
+ signature = await account.signMessage({ message });
94
+ } else {
95
+ throw new Error(
96
+ "Either a valid privateKey or options.signer must be provided"
97
+ );
98
+ }
99
+ return `${message}&sig=${signature}`;
100
+ }
101
+ function parseSignedQR(uri) {
102
+ const match = uri.match(URI_PATTERN);
103
+ if (!match) {
104
+ return null;
105
+ }
106
+ const [, profileAddress, chainIdStr, timestampStr, signature] = match;
107
+ return {
108
+ profileAddress: profileAddress.toLowerCase(),
109
+ chainId: Number.parseInt(chainIdStr, 10),
110
+ timestamp: Number.parseInt(timestampStr, 10),
111
+ signature,
112
+ message: createMessage(
113
+ profileAddress,
114
+ Number.parseInt(chainIdStr, 10),
115
+ Number.parseInt(timestampStr, 10)
116
+ )
117
+ };
118
+ }
119
+ async function verifySignedQR(uri, options) {
120
+ const maxAgeSeconds = options?.maxAgeSeconds ?? 60;
121
+ const skipTimestampValidation = options?.skipTimestampValidation ?? false;
122
+ const parsed = parseSignedQR(uri);
123
+ if (!parsed) {
124
+ throw new Error("Invalid QR format: does not match expected URI pattern");
125
+ }
126
+ const { profileAddress, chainId, timestamp, signature, message } = parsed;
127
+ const messageHash = hashMessage(message);
128
+ const recoveredAddress = await recoverMessageAddress({
129
+ message,
130
+ signature
131
+ });
132
+ const now = Math.floor(Date.now() / 1e3);
133
+ const isExpired = !skipTimestampValidation && timestamp + maxAgeSeconds < now;
134
+ return {
135
+ isValid: !isExpired,
136
+ profileAddress,
137
+ chainId,
138
+ timestamp,
139
+ recoveredAddress: recoveredAddress.toLowerCase(),
140
+ isExpired,
141
+ message,
142
+ messageHash
143
+ };
144
+ }
145
+ function isSignedQRFormat(uri) {
146
+ return URI_PATTERN.test(uri);
147
+ }
148
+ function getControllerAddress(privateKey) {
149
+ const account = privateKeyToAccount(privateKey);
150
+ return account.address;
151
+ }
152
+ var EIP1271_MAGIC_VALUE = "0x1626ba7e";
153
+ function getEIP1271Data(uri, verificationResult) {
154
+ const parsed = parseSignedQR(uri);
155
+ if (!parsed) {
156
+ throw new Error("Invalid QR format");
157
+ }
158
+ return {
159
+ hash: verificationResult.messageHash,
160
+ signature: parsed.signature
161
+ };
162
+ }
163
+
71
164
  // src/utils/slug.ts
72
165
  var slug = (value) => {
73
166
  if (!value) {
@@ -152,8 +245,16 @@ var UrlResolver = class {
152
245
  export {
153
246
  EXTENSION_STORE_LINKS,
154
247
  browserInfo,
248
+ createMessage,
249
+ createSignedQR,
250
+ parseSignedQR,
251
+ verifySignedQR,
252
+ isSignedQRFormat,
253
+ getControllerAddress,
254
+ EIP1271_MAGIC_VALUE,
255
+ getEIP1271Data,
155
256
  slug,
156
257
  UrlConverter,
157
258
  UrlResolver
158
259
  };
159
- //# sourceMappingURL=chunk-GFLV5EJV.js.map
260
+ //# sourceMappingURL=chunk-RECO5F6T.js.map