@lumiapassport/ui-kit 1.6.3 → 1.8.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 CHANGED
@@ -340,6 +340,98 @@ function DirectTransactionExample() {
340
340
  - ✅ Use `useSendTransaction()` hook for React components (automatic session management)
341
341
  - ✅ Use `sendUserOperation()` function for custom logic, utility functions, or non-React code
342
342
 
343
+ ### signTypedData - Sign EIP712 Structured Messages
344
+
345
+ Sign structured data according to [EIP-712](https://eips.ethereum.org/EIPS/eip-712) standard. This is commonly used for off-chain signatures in dApps (e.g., NFT marketplace orders, gasless transactions, permit signatures).
346
+
347
+ ```tsx
348
+ import { signTypedData, useLumiaPassportSession } from '@lumiapassport/ui-kit';
349
+
350
+ function SignatureExample() {
351
+ const { session } = useLumiaPassportSession();
352
+
353
+ const handleSign = async () => {
354
+ if (!session) return;
355
+
356
+ try {
357
+ // Define EIP712 typed data
358
+ const signature = await signTypedData(session, {
359
+ domain: {
360
+ name: 'MyDApp', // Your dApp name (must match contract)
361
+ version: '1', // Contract version
362
+ chainId: 994, // Lumia Prism Testnet
363
+ verifyingContract: '0x...', // Your contract address (REQUIRED in production!)
364
+ },
365
+ types: {
366
+ Order: [
367
+ { name: 'tokenIds', type: 'uint256[]' },
368
+ { name: 'price', type: 'uint256' },
369
+ { name: 'deadline', type: 'uint256' },
370
+ ],
371
+ },
372
+ primaryType: 'Order',
373
+ message: {
374
+ tokenIds: [1n, 2n, 3n],
375
+ price: 1000000000000000000n, // 1 token in wei
376
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now
377
+ },
378
+ });
379
+
380
+ console.log('Signature:', signature);
381
+
382
+ // Verify signature (optional)
383
+ const { recoverTypedDataAddress } = await import('viem');
384
+ const recoveredAddress = await recoverTypedDataAddress({
385
+ domain: { /* same domain */ },
386
+ types: { /* same types */ },
387
+ primaryType: 'Order',
388
+ message: { /* same message */ },
389
+ signature,
390
+ });
391
+
392
+ console.log('Signer:', recoveredAddress); // Should match session.ownerAddress
393
+ } catch (error) {
394
+ console.error('Signing failed:', error);
395
+ }
396
+ };
397
+
398
+ return <button onClick={handleSign}>Sign Message</button>;
399
+ }
400
+ ```
401
+
402
+ **Important Notes about ERC-4337 Smart Accounts:**
403
+
404
+ In Account Abstraction (ERC-4337), there are **two addresses**:
405
+ 1. **Owner Address (EOA)** - The address that signs messages/transactions
406
+ 2. **Smart Account Address** - The contract wallet address
407
+
408
+ ⚠️ **Critical:** The signature is created by the **owner address** (EOA), NOT the smart account address!
409
+
410
+ **Compatibility with existing protocols:**
411
+ - ✅ **Works:** Protocols that verify signatures off-chain (e.g., your backend verifies the owner EOA signature)
412
+ - ⚠️ **May not work:** Protocols designed for EOA wallets that store and verify against `msg.sender` or wallet address
413
+ - Example: Uniswap Permit2, some NFT marketplaces
414
+ - These protocols expect the signer address to match the wallet address
415
+ - With smart accounts: signer = owner EOA, wallet = smart account contract
416
+ - **Solution:** Use ERC-1271 signature validation in your smart contracts (allows contracts to validate signatures)
417
+
418
+ **Domain Configuration:**
419
+ - In production, use your actual `verifyingContract` address (not zero address!)
420
+ - The `domain` parameters must match exactly between frontend and smart contract
421
+ - The `chainId` should match the network you're deploying to
422
+
423
+ **Technical Details:**
424
+ - Shows a MetaMask-like confirmation modal with structured message preview
425
+ - All BigInt values are supported in the message
426
+ - Signature can be verified using `viem.recoverTypedDataAddress()` - will return owner EOA address
427
+
428
+ **When to use signTypedData:**
429
+ - ✅ Custom backend signature verification (you control the verification logic)
430
+ - ✅ Gasless transactions with meta-transaction relayers
431
+ - ✅ DAO voting and governance (off-chain signatures)
432
+ - ✅ Custom smart contracts with ERC-1271 support
433
+ - ⚠️ Be cautious with protocols designed exclusively for EOA wallets
434
+
343
435
  ### prepareUserOperation - Prepare for Backend Submission
344
436
 
345
437
  ```tsx
@@ -15,7 +15,7 @@
15
15
  <meta http-equiv="X-Content-Type-Options" content="nosniff" />
16
16
  <meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin" />
17
17
 
18
- <title>Lumia Passport Secure Wallet - iframe version 1.6.3</title>
18
+ <title>Lumia Passport Secure Wallet - iframe version 1.8.0</title>
19
19
 
20
20
  <!-- Styles will be injected by build process -->
21
21
  <style>
@@ -28,6 +28,10 @@
28
28
  --iframe-modal-bg: white;
29
29
  --iframe-button-bg: #667eea;
30
30
  --iframe-button-text: white;
31
+ --iframe-section-bg: #f9fafb;
32
+ --iframe-section-border: #e5e7eb;
33
+ --iframe-section-text: #374151;
34
+ --iframe-field-label: #6b7280;
31
35
  }
32
36
 
33
37
  * {
@@ -542,6 +546,221 @@
542
546
  .trust-app-label:hover {
543
547
  color: #111827;
544
548
  }
549
+
550
+ /* EIP712 Signature Request Modal Styles */
551
+ .eip712-confirmation-modal {
552
+ position: fixed;
553
+ top: 0;
554
+ left: 0;
555
+ right: 0;
556
+ bottom: 0;
557
+ z-index: 10000;
558
+ }
559
+
560
+ .eip712-modal {
561
+ max-width: 480px;
562
+ }
563
+
564
+ .eip712-content {
565
+ padding: 1.5rem 2rem;
566
+ }
567
+
568
+ .section-title {
569
+ font-size: 0.875rem;
570
+ font-weight: 600;
571
+ color: var(--iframe-section-text);
572
+ margin-bottom: 0.75rem;
573
+ text-transform: uppercase;
574
+ letter-spacing: 0.05em;
575
+ }
576
+
577
+ .eip712-section {
578
+ background: var(--iframe-section-bg);
579
+ border: 1px solid var(--iframe-section-border);
580
+ border-radius: 8px;
581
+ padding: 1rem;
582
+ margin-bottom: 1rem;
583
+ }
584
+
585
+ .section-subtitle {
586
+ font-size: 1rem;
587
+ font-weight: 700;
588
+ color: var(--iframe-section-text);
589
+ margin-bottom: 0.75rem;
590
+ padding-bottom: 0.5rem;
591
+ border-bottom: 1px solid var(--iframe-section-border);
592
+ }
593
+
594
+ .eip712-field {
595
+ display: grid;
596
+ grid-template-columns: 120px 1fr;
597
+ gap: 0.75rem;
598
+ padding: 0.5rem 0;
599
+ align-items: start;
600
+ }
601
+
602
+ .eip712-field:not(:last-child) {
603
+ border-bottom: 1px solid var(--iframe-section-border);
604
+ }
605
+
606
+ .field-name {
607
+ font-size: 0.8125rem;
608
+ font-weight: 500;
609
+ color: var(--iframe-field-label);
610
+ }
611
+
612
+ .field-value {
613
+ font-size: 0.8125rem;
614
+ color: var(--iframe-section-text);
615
+ word-break: break-word;
616
+ font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
617
+ }
618
+
619
+ .eip712-details {
620
+ margin-bottom: 1rem;
621
+ border: 1px solid var(--iframe-section-border);
622
+ border-radius: 6px;
623
+ overflow: hidden;
624
+ }
625
+
626
+ .eip712-details summary {
627
+ padding: 0.75rem 1rem;
628
+ cursor: pointer;
629
+ background: var(--iframe-section-bg);
630
+ font-size: 0.875rem;
631
+ font-weight: 600;
632
+ color: var(--iframe-section-text);
633
+ user-select: none;
634
+ list-style: none;
635
+ display: flex;
636
+ align-items: center;
637
+ justify-content: space-between;
638
+ }
639
+
640
+ .eip712-details summary::-webkit-details-marker {
641
+ display: none;
642
+ }
643
+
644
+ .eip712-details summary:after {
645
+ content: '▼';
646
+ font-size: 0.7rem;
647
+ transition: transform 0.2s;
648
+ color: var(--iframe-section-text);
649
+ }
650
+
651
+ .eip712-details[open] summary:after {
652
+ transform: rotate(180deg);
653
+ }
654
+
655
+ .eip712-details summary:hover {
656
+ background: var(--iframe-modal-bg);
657
+ opacity: 0.9;
658
+ }
659
+
660
+ .eip712-raw {
661
+ margin: 0;
662
+ padding: 1rem;
663
+ background: var(--iframe-modal-bg);
664
+ border-top: 1px solid var(--iframe-section-border);
665
+ max-height: 200px;
666
+ overflow-y: auto;
667
+ }
668
+
669
+ .eip712-raw code {
670
+ font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
671
+ font-size: 0.75rem;
672
+ color: var(--iframe-section-text);
673
+ line-height: 1.5;
674
+ white-space: pre-wrap;
675
+ word-break: break-all;
676
+ }
677
+
678
+ .security-info {
679
+ background: var(--iframe-section-bg);
680
+ border-radius: 6px;
681
+ padding: 0.75rem 1rem;
682
+ margin-bottom: 1rem;
683
+ }
684
+
685
+ .info-item {
686
+ display: flex;
687
+ justify-content: space-between;
688
+ align-items: center;
689
+ font-size: 0.8125rem;
690
+ }
691
+
692
+ .info-label {
693
+ font-weight: 500;
694
+ color: var(--iframe-field-label);
695
+ }
696
+
697
+ .info-value {
698
+ font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
699
+ color: var(--iframe-section-text);
700
+ }
701
+
702
+ .footer-notice {
703
+ padding: 0 2rem 2rem;
704
+ text-align: center;
705
+ }
706
+
707
+ .modal-title {
708
+ font-size: 1.25rem;
709
+ font-weight: 600;
710
+ color: var(--iframe-text);
711
+ margin: 0.5rem 0;
712
+ }
713
+
714
+ .origin-text {
715
+ font-size: 0.875rem;
716
+ color: var(--iframe-text-secondary);
717
+ margin: 0;
718
+ display: flex;
719
+ align-items: center;
720
+ justify-content: center;
721
+ gap: 0.5rem;
722
+ }
723
+
724
+ .verified-badge {
725
+ color: #10b981;
726
+ font-weight: 600;
727
+ }
728
+
729
+ .project-logo {
730
+ width: 48px;
731
+ height: 48px;
732
+ border-radius: 8px;
733
+ object-fit: cover;
734
+ }
735
+
736
+ .project-logo-placeholder {
737
+ width: 48px;
738
+ height: 48px;
739
+ border-radius: 8px;
740
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
741
+ display: flex;
742
+ align-items: center;
743
+ justify-content: center;
744
+ font-size: 1.5rem;
745
+ }
746
+
747
+ .arrow-icon {
748
+ font-size: 1.25rem;
749
+ color: #9ca3af;
750
+ }
751
+
752
+ .lumia-logo {
753
+ width: 40px;
754
+ height: 40px;
755
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
756
+ border-radius: 8px;
757
+ display: flex;
758
+ align-items: center;
759
+ justify-content: center;
760
+ color: white;
761
+ font-weight: 700;
762
+ font-size: 1.25rem;
763
+ }
545
764
  </style>
546
765
  </head>
547
766
  <body>
@@ -1085,6 +1085,7 @@ async function uploadShareToVault(encryptedShare, accessToken) {
1085
1085
  "Authorization": `Bearer ${token}`,
1086
1086
  "Idempotency-Key": idempotencyKey
1087
1087
  },
1088
+ credentials: "include",
1088
1089
  body: JSON.stringify(encryptedShare)
1089
1090
  });
1090
1091
  if (!response.ok) {
@@ -1109,7 +1110,8 @@ async function downloadShareFromVault(accessToken) {
1109
1110
  "Authorization": `Bearer ${token}`,
1110
1111
  "X-Client-Device-Id": "lumia-ui-kit",
1111
1112
  "X-Client-Device-Name": "Lumia UI Kit"
1112
- }
1113
+ },
1114
+ credentials: "include"
1113
1115
  });
1114
1116
  if (!response.ok) {
1115
1117
  if (response.status === 404) {
@@ -1379,6 +1381,7 @@ var SecureMessenger = class {
1379
1381
  "AUTHENTICATE",
1380
1382
  "START_DKG",
1381
1383
  "SIGN_TRANSACTION",
1384
+ "SIGN_TYPED_DATA",
1382
1385
  "GET_ADDRESS",
1383
1386
  "CHECK_KEYSHARE",
1384
1387
  "GET_TRUSTED_APPS",
@@ -2628,7 +2631,9 @@ var SigningManager = class extends TokenRefreshApiClient {
2628
2631
  return cached.data;
2629
2632
  }
2630
2633
  try {
2631
- const response = await fetch(`${this.METADATA_API_URL}/${projectId}/metadata`);
2634
+ const response = await fetch(`${this.METADATA_API_URL}/${projectId}/metadata`, {
2635
+ credentials: "include"
2636
+ });
2632
2637
  if (!response.ok) {
2633
2638
  console.warn(`[iframe][Sign] Failed to fetch project metadata: ${response.status}`);
2634
2639
  return null;
@@ -2911,14 +2916,22 @@ var SigningManager = class extends TokenRefreshApiClient {
2911
2916
  }
2912
2917
  /**
2913
2918
  * Assess transaction risk
2919
+ *
2920
+ * TODO: Implement backend-based risk assessment with real-time price conversion
2921
+ * - Get current LUMIA/USD price from API
2922
+ * - Calculate transaction value in USD
2923
+ * - Use dynamic thresholds based on USD value (e.g., >$100 = HIGH)
2924
+ * - Consider additional factors: recipient reputation, contract verification, etc.
2914
2925
  */
2915
2926
  async assessRisk(transaction) {
2916
2927
  const reasons = [];
2917
2928
  let score = 0;
2918
- const valueEth = parseFloat(transaction.value);
2919
- if (valueEth > 1) {
2929
+ const valueWei = BigInt(transaction.value || "0");
2930
+ const valueLumia = Number(valueWei) / 1e18;
2931
+ if (valueLumia > 10) {
2920
2932
  score += 20;
2921
- reasons.push(`High value transaction (${valueEth} ETH)`);
2933
+ const formattedValue = valueLumia >= 1 ? valueLumia.toFixed(4).replace(/\.?0+$/, "") : valueLumia.toFixed(8).replace(/\.?0+$/, "");
2934
+ reasons.push(`High value transaction (${formattedValue} LUMIA)`);
2922
2935
  }
2923
2936
  if (transaction.data && transaction.data !== "0x" && transaction.data.length > 2) {
2924
2937
  score += 10;
@@ -2975,6 +2988,170 @@ var SigningManager = class extends TokenRefreshApiClient {
2975
2988
  extractServerMessage(response) {
2976
2989
  return response?.msgNext || response?.msg_next || response?.msg || null;
2977
2990
  }
2991
+ /**
2992
+ * Sign EIP712 typed data with user confirmation
2993
+ */
2994
+ async signTypedData(userId, projectId, origin, typedData, digest32, accessToken) {
2995
+ console.log("[iframe][Sign] EIP712 signing request:", { userId, projectId, origin, primaryType: typedData.primaryType });
2996
+ await this.initialize();
2997
+ const projectInfo = {
2998
+ id: projectId,
2999
+ name: "Application",
3000
+ logoUrl: "",
3001
+ website: origin,
3002
+ domains: [origin]
3003
+ };
3004
+ const isTrusted = this.trustedApps.isTrusted(userId, projectId, origin);
3005
+ if (!isTrusted) {
3006
+ const confirmResult = await this.showEIP712ConfirmationDialog(
3007
+ userId,
3008
+ projectId,
3009
+ projectInfo,
3010
+ origin,
3011
+ typedData
3012
+ );
3013
+ if (!confirmResult.confirmed) {
3014
+ throw new Error("User rejected signature request");
3015
+ }
3016
+ if (confirmResult.trustApp) {
3017
+ this.trustedApps.addTrustedApp(userId, projectId, origin);
3018
+ }
3019
+ }
3020
+ const keyshareData = this.storage.loadKeyshare(userId);
3021
+ if (!keyshareData) {
3022
+ throw new Error("No keyshare found. Please complete DKG first.");
3023
+ }
3024
+ const signature = await this.performMPCSigning(userId, keyshareData, digest32, projectId, accessToken);
3025
+ console.log("[iframe][Sign] EIP712 signature generated");
3026
+ return signature;
3027
+ }
3028
+ /**
3029
+ * Show EIP712 confirmation dialog
3030
+ */
3031
+ async showEIP712ConfirmationDialog(userId, projectId, project, origin, typedData) {
3032
+ this.showIframe();
3033
+ const metadata = await this.fetchProjectMetadata(projectId);
3034
+ return new Promise((resolve) => {
3035
+ const modal = this.createEIP712ConfirmationModal(userId, projectId, project, origin, typedData, metadata);
3036
+ const confirmBtn = modal.querySelector(".confirm-btn");
3037
+ const cancelBtn = modal.querySelector(".cancel-btn");
3038
+ const trustCheckbox = modal.querySelector(".trust-app-checkbox");
3039
+ confirmBtn?.addEventListener("click", (e) => {
3040
+ if (e.isTrusted) {
3041
+ const trustApp = trustCheckbox?.checked || false;
3042
+ modal.remove();
3043
+ this.hideIframe();
3044
+ resolve({ confirmed: true, trustApp });
3045
+ }
3046
+ });
3047
+ cancelBtn?.addEventListener("click", () => {
3048
+ modal.remove();
3049
+ this.hideIframe();
3050
+ resolve({ confirmed: false, trustApp: false });
3051
+ });
3052
+ document.body.appendChild(modal);
3053
+ });
3054
+ }
3055
+ /**
3056
+ * Create EIP712 confirmation modal UI (similar to MetaMask)
3057
+ */
3058
+ createEIP712ConfirmationModal(userId, projectId, project, origin, typedData, metadata) {
3059
+ const modal = document.createElement("div");
3060
+ modal.className = "eip712-confirmation-modal";
3061
+ const isVerifiedOrigin = project.domains.includes(origin);
3062
+ const formatFieldValue = (value) => {
3063
+ if (Array.isArray(value)) {
3064
+ return `[${value.map((v) => formatFieldValue(v)).join(", ")}]`;
3065
+ }
3066
+ if (typeof value === "bigint") {
3067
+ return value.toString();
3068
+ }
3069
+ if (typeof value === "object" && value !== null) {
3070
+ return JSON.stringify(value, null, 2);
3071
+ }
3072
+ return String(value);
3073
+ };
3074
+ const messageFields = Object.entries(typedData.message).map(([key, value]) => `
3075
+ <div class="eip712-field">
3076
+ <div class="field-name">${key}:</div>
3077
+ <div class="field-value">${formatFieldValue(value)}</div>
3078
+ </div>
3079
+ `).join("");
3080
+ const domainFields = Object.entries(typedData.domain).filter(([_, value]) => value !== void 0).map(([key, value]) => `
3081
+ <div class="eip712-field">
3082
+ <div class="field-name">${key}:</div>
3083
+ <div class="field-value">${formatFieldValue(value)}</div>
3084
+ </div>
3085
+ `).join("");
3086
+ modal.innerHTML = `
3087
+ <div class="modal-overlay">
3088
+ <div class="modal-content eip712-modal">
3089
+ <!-- Header -->
3090
+ <div class="auth-header">
3091
+ <div class="logo-container">
3092
+ ${metadata?.logo ? `<img src="${metadata.logo}" alt="${metadata.name}" class="project-logo" />` : '<div class="project-logo-placeholder">\u{1F510}</div>'}
3093
+ <span class="arrow-icon">\u2192</span>
3094
+ <div class="lumia-logo">L</div>
3095
+ </div>
3096
+ <h2 class="modal-title">Signature Request</h2>
3097
+ <p class="origin-text">
3098
+ ${isVerifiedOrigin ? '<span class="verified-badge">\u2713</span>' : ""}
3099
+ ${origin}
3100
+ </p>
3101
+ </div>
3102
+
3103
+ <!-- EIP712 Message Content -->
3104
+ <div class="eip712-content">
3105
+ <div class="section-title">\u{1F4DD} Message</div>
3106
+ <div class="eip712-section">
3107
+ <div class="section-subtitle">${typedData.primaryType}</div>
3108
+ ${messageFields}
3109
+ </div>
3110
+
3111
+ <details class="eip712-details">
3112
+ <summary>\u{1F50D} Domain</summary>
3113
+ <div class="eip712-section">
3114
+ ${domainFields}
3115
+ </div>
3116
+ </details>
3117
+
3118
+ <details class="eip712-details">
3119
+ <summary>\u{1F4CB} Full Message</summary>
3120
+ <pre class="eip712-raw"><code>${JSON.stringify(typedData.message, null, 2)}</code></pre>
3121
+ </details>
3122
+ </div>
3123
+
3124
+ <!-- Security Info -->
3125
+ <div class="security-info">
3126
+ <div class="info-item">
3127
+ <span class="info-label">Signing with:</span>
3128
+ <span class="info-value">${this.storage.getOwnerAddress(userId) || userId.substring(0, 20) + "..."}</span>
3129
+ </div>
3130
+ </div>
3131
+
3132
+ <!-- Trust App Option -->
3133
+ <div class="trust-app-section">
3134
+ <label class="trust-app-label">
3135
+ <input type="checkbox" class="trust-app-checkbox" />
3136
+ <span>Trust this application and skip confirmation for future signatures</span>
3137
+ </label>
3138
+ </div>
3139
+
3140
+ <!-- Action Buttons -->
3141
+ <div class="actions">
3142
+ <button class="cancel-btn">Reject</button>
3143
+ <button class="confirm-btn">Sign</button>
3144
+ </div>
3145
+
3146
+ <!-- Footer Notice -->
3147
+ <div class="footer-notice">
3148
+ <p class="footer-note">Only sign messages from applications you trust.</p>
3149
+ </div>
3150
+ </div>
3151
+ </div>
3152
+ `;
3153
+ return modal;
3154
+ }
2978
3155
  /**
2979
3156
  * Show iframe (notify parent)
2980
3157
  */
@@ -3014,7 +3191,9 @@ var AuthorizationManager = class {
3014
3191
  return cached.data;
3015
3192
  }
3016
3193
  try {
3017
- const response = await fetch(`${this.METADATA_API_URL}/${projectId}/metadata`);
3194
+ const response = await fetch(`${this.METADATA_API_URL}/${projectId}/metadata`, {
3195
+ credentials: "include"
3196
+ });
3018
3197
  if (!response.ok) {
3019
3198
  console.warn(`[iframe][Auth] Failed to fetch project metadata: ${response.status}`);
3020
3199
  return null;
@@ -3434,7 +3613,8 @@ var GoogleDriveProvider = class {
3434
3613
  const searchResponse = await fetch(
3435
3614
  `https://www.googleapis.com/drive/v3/files?q=name='${folderName}' and mimeType='application/vnd.google-apps.folder' and trashed=false`,
3436
3615
  {
3437
- headers: { Authorization: `Bearer ${this.accessToken}` }
3616
+ headers: { Authorization: `Bearer ${this.accessToken}` },
3617
+ credentials: "include"
3438
3618
  }
3439
3619
  );
3440
3620
  if (!searchResponse.ok) {
@@ -3450,6 +3630,7 @@ var GoogleDriveProvider = class {
3450
3630
  Authorization: `Bearer ${this.accessToken}`,
3451
3631
  "Content-Type": "application/json"
3452
3632
  },
3633
+ credentials: "include",
3453
3634
  body: JSON.stringify({
3454
3635
  name: folderName,
3455
3636
  mimeType: "application/vnd.google-apps.folder"
@@ -3473,6 +3654,7 @@ var GoogleDriveProvider = class {
3473
3654
  headers: {
3474
3655
  Authorization: `Bearer ${this.accessToken}`
3475
3656
  },
3657
+ credentials: "include",
3476
3658
  body: form
3477
3659
  }
3478
3660
  );
@@ -3748,7 +3930,7 @@ var BackupManager = class {
3748
3930
  };
3749
3931
 
3750
3932
  // src/iframe/main.ts
3751
- var IFRAME_VERSION = "1.6.3";
3933
+ var IFRAME_VERSION = "1.8.0";
3752
3934
  var IframeWallet = class {
3753
3935
  constructor() {
3754
3936
  console.log("=".repeat(60));
@@ -3831,6 +4013,9 @@ var IframeWallet = class {
3831
4013
  case "SIGN_TRANSACTION":
3832
4014
  await this.handleSignTransaction(message, origin);
3833
4015
  break;
4016
+ case "SIGN_TYPED_DATA":
4017
+ await this.handleSignTypedData(message, origin);
4018
+ break;
3834
4019
  case "GET_ADDRESS":
3835
4020
  await this.handleGetAddress(message, origin);
3836
4021
  break;
@@ -4005,6 +4190,40 @@ var IframeWallet = class {
4005
4190
  throw error;
4006
4191
  }
4007
4192
  }
4193
+ async handleSignTypedData(message, origin) {
4194
+ const { sessionToken, userId, projectId, typedData, digest32, accessToken } = message.data;
4195
+ const { messageId } = message;
4196
+ if (!this.sessionManager.validateSession(sessionToken, origin)) {
4197
+ throw new Error("Invalid session");
4198
+ }
4199
+ const isAuthorized = await this.authManager.checkAuthorization(userId, projectId);
4200
+ if (!isAuthorized) {
4201
+ throw new Error("User has not authorized this application");
4202
+ }
4203
+ console.log(`[iframe] SIGN_TYPED_DATA: userId=${userId}, primaryType=${typedData?.primaryType}`);
4204
+ try {
4205
+ const signature = await this.signingManager.signTypedData(
4206
+ userId,
4207
+ projectId,
4208
+ origin,
4209
+ typedData,
4210
+ digest32,
4211
+ accessToken
4212
+ );
4213
+ this.messenger.sendResponse(
4214
+ messageId,
4215
+ {
4216
+ type: "LUMIA_PASSPORT_EIP712_SIGNATURE",
4217
+ signature
4218
+ },
4219
+ origin
4220
+ );
4221
+ console.log(`[iframe] \u2705 EIP712 message signed`);
4222
+ } catch (error) {
4223
+ console.error("[iframe] EIP712 signing failed:", error);
4224
+ throw error;
4225
+ }
4226
+ }
4008
4227
  async handleGetAddress(message, origin) {
4009
4228
  const { sessionToken, userId } = message.data;
4010
4229
  const { messageId } = message;