@lumiapassport/ui-kit 1.14.8 → 1.14.10

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.
@@ -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.14.8</title>
18
+ <title>Lumia Passport Secure Wallet - iframe version 1.14.10</title>
19
19
 
20
20
  <!-- Styles will be injected by build process -->
21
21
  <style>
@@ -608,39 +608,46 @@
608
608
  }
609
609
 
610
610
  /* Trust app checkbox section */
611
+ /* OK */
611
612
  .trust-app-section {
612
- margin: 1.5rem 2rem;
613
- padding: 0.75rem;
614
- background: #f9fafb;
615
- border-radius: 6px;
616
- border: 1px solid #e5e7eb;
613
+ width: 100%;
614
+ padding: var(--iframe-gap);
615
+ background: var(--iframe-info);
616
+ border-radius: var(--iframe-el-bdrs);
617
+ border: 1px solid var(--iframe-bd);
618
+ transition: opacity 0.2s ease;
619
+ }
620
+ .trust-app-section:hover {
621
+ opacity: 0.6;
622
+ }
623
+ .trust-app-section:active {
624
+ opacity: 0.4;
617
625
  }
618
626
 
627
+ /* OK */
619
628
  .trust-app-label {
620
629
  display: flex;
621
- align-items: center;
622
- gap: 0.5rem;
630
+ align-items: flex-start;
631
+ gap: var(--iframe-gap);
623
632
  cursor: pointer;
624
633
  font-size: 0.9rem;
625
- color: #374151;
626
634
  }
627
635
 
636
+ /* OK */
628
637
  .trust-app-checkbox {
629
- width: 18px;
630
- height: 18px;
638
+ width: 24px;
639
+ height: 24px;
631
640
  cursor: pointer;
632
641
  flex-shrink: 0;
633
642
  }
634
643
 
635
644
  .trust-app-label span {
636
- line-height: 1.4;
645
+ font-weight: 600;
646
+ font-size: 14px;
647
+ line-height: 1.2;
637
648
  user-select: none;
638
649
  }
639
650
 
640
- .trust-app-label:hover {
641
- color: #111827;
642
- }
643
-
644
651
  /* EIP712 Signature Request Modal Styles */
645
652
  .eip712-confirmation-modal {
646
653
  position: fixed;
@@ -1269,9 +1269,15 @@ var TemplateEngine = class {
1269
1269
  // src/iframe/templates/html/authorization.html
1270
1270
  var authorization_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n <svg\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n stroke="var(--iframe-text-secondary)"\n stroke-width="2"\n stroke-linecap="round"\n stroke-linejoin="round"\n >\n <path d="M5 12h14" />\n <path d="m12 5 7 7-7 7" />\n </svg>\n <!-- ------------------------------------------------------------ -->\n\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n </div>\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h2-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >{{displayName}}</span\n >\n </div>\n\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusIcon}} {{origin}}</span\n >\n </div>\n\n <!-- Permissions box -->\n <div\n id="data-auth-permissions-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="data-auth-permissions-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Permissions \u25BC\n </div>\n\n <div\n id="data-auth-permissions-content"\n style="width: 100%; height: var(--iframe-permissions-h, 0); overflow: hidden; transition: height 300ms ease"\n >\n <div\n style="\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n <div style="margin-bottom: var(--iframe-gap)">\n <strong>{{displayName}}</strong> by <span class="project-owner">{{displayName}}</span> wants to access your\n <strong>Lumia Passport</strong> account\n </div>\n\n <div style="margin-bottom: var(--iframe-gap)">\n <strong style="margin-bottom: var(--iframe-gap)">Personal user data</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Wallet address (read-only)</li>\n <li style="padding-left: var(--iframe-pd)">Public transaction history (read-only)</li>\n </ul>\n </div>\n\n <div style="width: 100%">\n <strong style="margin-bottom: var(--iframe-gap)">Permissions</strong>\n <ul style="list-style: none">\n <li style="padding-left: var(--iframe-pd)">Request transaction signatures</li>\n <li style="padding-left: var(--iframe-pd)">Initiate blockchain operations</li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n {{#if showSecurityWarning}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F Warning:</strong> This domain is not verified for {{displayName}}\n <div>Expected: {{expectedDomains}}</div>\n <div>Actual: {{origin}}</div>\n </div>\n {{else}}\n <span style="display: block; text-align: center; font-size: 10px; color: var(--iframe-text-secondary)"\n >By continuing you allow the app to use these permissions, you can revoke access at any time.</span\n >\n {{/if}}\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Cancel</button>\n <button class="authorize-btn" {{authorizeButtonState}}>Continue</button>\n </div>\n </div>\n</div>\n';
1271
1271
 
1272
+ // src/iframe/templates/html/confirm-tx.html
1273
+ var confirm_tx_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <!-- Header with logos -->\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n {{#if displayLogo}}\n <img\n src="{{displayLogo}}"\n alt="{{displayName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >A</span\n >\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n <svg\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n stroke="var(--iframe-text-secondary)"\n stroke-width="2"\n stroke-linecap="round"\n stroke-linejoin="round"\n >\n <path d="M5 12h14" />\n <path d="m12 5 7 7-7 7" />\n </svg>\n <!-- ------------------------------------------------------------ -->\n\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n\n <!-- Transaction title -->\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h1-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >Confirm Transaction</span\n >\n </div>\n\n <!-- Application info -->\n <div class="domain-info {{domainStatusClass}}">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace"\n >{{domainStatusText}} {{origin}}</span\n >\n </div>\n\n <!-- Transaction details box -->\n <div\n id="confirm-transaction-details-block"\n aria-expanded="false"\n style="display: flex; flex-direction: column; width: 100%"\n >\n <div\n id="confirm-transaction-details-toggler"\n style="\n cursor: pointer;\n text-align: center;\n list-style: none;\n padding: 0 var(--iframe-pd);\n user-select: none;\n font-size: 12px;\n line-height: 20px;\n "\n >\n Show Transaction Details \u25BC\n </div>\n\n <div\n id="confirm-transaction-details-content"\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n max-height: var(--confirm-transaction-details-h, 0px);\n overflow: hidden;\n transition: max-height 300ms ease;\n "\n >\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n width: 100%;\n padding: var(--iframe-gap);\n border-radius: var(--iframe-el-bdrs);\n background: var(--iframe-info);\n font-size: 10px;\n "\n >\n {{#if fromAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">From:</strong>\n <code style="word-break: break-all; display: block">{{fromAddress}}</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if toAddress}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">To:</strong>\n <code style="word-break: break-all; display: block">{{toAddress}}</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if hasValue}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Amount:</strong>\n <code style="word-break: break-all; display: block">{{formattedValue}} LUMIA</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showContractInteraction}}\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Action:</strong>\n <code style="word-break: break-all; display: block">Contract Interaction</code>\n </div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div style="display: flex; justify-content: space-between">\n <strong style="color: var(--iframe-text-secondary); display: block">Estimated Cost:</strong>\n <code style="word-break: break-all; display: block; color: #10b981">{{estimatedCost}}</code>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if isSponsored}}\n <div\n style="\n display: flex;\n justify-content: space-between;\n background: var(--iframe-success);\n border-radius: var(--iframe-el-bdrs);\n padding: var(--iframe-gap);\n "\n >\n <span>Gas fees will be paid by the application (no cost to you)</span>\n </div>\n {{/if}}\n </div>\n </div>\n </div>\n\n <!-- ------------------------------------------------------------ -->\n\n {{#if showHighRisk}}\n <div class="security-warning">\n <strong>\u26A0\uFE0F {{riskLevel}} RISK</strong>\n {{riskReasons}}\n </div>\n {{else}}\n <div class="security-warning">Please verify this transaction carefully before confirming.</div>\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n\n <div class="trust-app-section">\n <label class="trust-app-label">\n <input type="checkbox" class="trust-app-checkbox" />\n <span>Add this application to trusted list and skip confirmation for future transactions</span>\n </label>\n </div>\n\n <!-- Action buttons -->\n <div style="display: flex; gap: var(--iframe-gap); width: 100%; align-items: center">\n <button class="cancel-btn">Reject</button>\n <button class="confirm-btn">Confirm</button>\n </div>\n\n <!-- Footer notice -->\n <span\n style="\n display: block;\n padding: var(--iframe-gap);\n text-align: center;\n font-size: 10px;\n color: var(--iframe-text-secondary);\n "\n >\n You can manage trusted applications in your Lumia Passport settings.\n </span>\n </div>\n</div>\n';
1274
+
1272
1275
  // src/iframe/templates/html/ready-indicator.html
1273
1276
  var ready_indicator_default = '<div class="ready-indicator">\n <div class="status-icon">\u2713</div>\n <h2>Secure Wallet Ready</h2>\n <p>Lumia Passport is ready for secure operations</p>\n <div class="info">\n <div class="info-row">\n <strong>Origin:</strong>\n <span>{{origin}}</span>\n </div>\n <div class="info-row">\n <strong>Status:</strong>\n <span class="status-active">Active</span>\n </div>\n <div class="info-row">\n <strong>Version:</strong>\n <span>{{iframeVersion}}</span>\n </div>\n <div class="info-row">\n <strong>Documentation:</strong>\n <a\n href="https://docs.lumiapassport.com/"\n target="_blank"\n rel="noopener noreferrer"\n style="color: #667eea; text-decoration: none"\n >docs.lumiapassport.com</a\n >\n </div>\n </div>\n</div>\n';
1274
1277
 
1278
+ // src/iframe/templates/html/sign-eip712-tx.html
1279
+ var sign_eip712_tx_default = '<div\n style="\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n background: #000000;\n "\n>\n <div\n style="\n display: flex;\n flex-direction: column;\n gap: var(--iframe-gap);\n align-items: center;\n background: var(--iframe-modal-bg);\n border-radius: 20px;\n padding: 20px;\n max-width: 320px;\n width: calc(100% - 40px);\n max-height: 90vh;\n overflow-y: auto;\n "\n >\n <!-- Header -->\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n {{#if metadataLogo}}\n <img\n src="{{metadataLogo}}"\n alt="{{metadataName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >\u{1F510}</span\n >\n {{/if}}\n\n <!-- ------------------------------------------------------------ -->\n <svg\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n stroke="var(--iframe-text-secondary)"\n stroke-width="2"\n stroke-linecap="round"\n stroke-linejoin="round"\n >\n <path d="M5 12h14" />\n <path d="m12 5 7 7-7 7" />\n </svg>\n <!-- ------------------------------------------------------------ -->\n\n {{#if userAvatar}}\n <img\n src="{{userAvatar}}"\n alt="{{userName}}"\n style="width: 48px; height: 48px; border-radius: 24px; object-fit: cover"\n />\n {{else}}\n <span\n style="\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 24px;\n background-color: var(--iframe-button-bg);\n color: var(--iframe-button-text);\n font-size: 14px;\n font-weight: 600;\n "\n >U</span\n >\n {{/if}}\n </div>\n\n <div style="display: flex; align-items: center; gap: var(--iframe-gap); justify-content: center">\n <span\n style="\n width: fit-content;\n font-size: var(--iframe-h2-fz);\n font-weight: var(--iframe-heading-fw);\n line-height: 150%;\n text-align: center;\n "\n >Signature Request</span\n >\n </div>\n\n <div class="domain-info verified">\n <span style="display: block; width: 100%; font-size: 12px; text-align: center; font-family: monospace">\n {{#if isVerifiedOrigin}}\n <span>\u2713</span>\n {{/if}} {{origin}}</span\n >\n </div>\n\n <!-- EIP712 Message Content -->\n <div class="eip712-content">\n <details class="eip712-details">\n <summary>Advanced details</summary>\n\n <div class="section-title">\u{1F4DD} Message</div>\n <div class="eip712-section">\n <div class="section-subtitle">{{primaryType}}</div>\n {{messageFields}}\n </div>\n\n <div class="section-title" style="margin-top: 16px">\u{1F50D} Domain</div>\n <div class="eip712-section">{{domainFields}}</div>\n\n <div class="section-title" style="margin-top: 16px">\u{1F4CB} Full Message</div>\n <pre class="eip712-raw"><code>{{fullMessageJson}}</code></pre>\n </details>\n </div>\n\n <!-- Trust App Option -->\n <div class="trust-app-section">\n <label class="trust-app-label">\n <input type="checkbox" class="trust-app-checkbox" />\n <span>Trust this application and skip confirmation for future signatures</span>\n </label>\n </div>\n\n <!-- Action Buttons -->\n <div class="actions">\n <button class="cancel-btn">Reject</button>\n <button class="confirm-btn">Sign</button>\n </div>\n\n <!-- Footer Notice -->\n <div class="footer-notice">\n <p class="footer-note">Only sign messages from applications you trust.</p>\n </div>\n </div>\n</div>\n';
1280
+
1275
1281
  // src/iframe/templates/template-loader.ts
1276
1282
  var TemplateLoader = class {
1277
1283
  /** Get template by name */
@@ -1293,6 +1299,10 @@ var TemplateLoader = class {
1293
1299
  return authorization_default;
1294
1300
  case "ready-indicator":
1295
1301
  return ready_indicator_default;
1302
+ case "sign-eip712-tx":
1303
+ return sign_eip712_tx_default;
1304
+ case "confirm-tx":
1305
+ return confirm_tx_default;
1296
1306
  default:
1297
1307
  return null;
1298
1308
  }
@@ -1347,6 +1357,115 @@ function buildReadyIndicatorData(input) {
1347
1357
  };
1348
1358
  }
1349
1359
 
1360
+ // src/iframe/templates/data/sign-tx-template-data.ts
1361
+ function buildSignTxTemplateData(project, origin, typedData, metadata, userProfile) {
1362
+ const isVerifiedOrigin = project.domains.includes(origin);
1363
+ const metadataName = metadata?.name || project.name;
1364
+ const metadataLogo = metadata?.logo || project.logoUrl || "";
1365
+ const userAvatar = userProfile?.avatar || "";
1366
+ const userName = userProfile?.displayName || "Lumia Passport";
1367
+ const formatFieldValue = (value) => {
1368
+ if (Array.isArray(value)) {
1369
+ return `[${value.map((v) => formatFieldValue(v)).join(", ")}]`;
1370
+ }
1371
+ if (typeof value === "bigint") {
1372
+ return value.toString();
1373
+ }
1374
+ if (typeof value === "object" && value !== null) {
1375
+ return JSON.stringify(value, null, 2);
1376
+ }
1377
+ return String(value);
1378
+ };
1379
+ const messageFields = Object.entries(typedData.message).map(
1380
+ ([key, value]) => `
1381
+ <div class="eip712-field">
1382
+ <div class="field-name">${key}:</div>
1383
+ <div class="field-value">${formatFieldValue(value)}</div>
1384
+ </div>
1385
+ `
1386
+ ).join("");
1387
+ const domainFields = Object.entries(typedData.domain).filter(([_, value]) => value !== void 0).map(
1388
+ ([key, value]) => `
1389
+ <div class="eip712-field">
1390
+ <div class="field-name">${key}:</div>
1391
+ <div class="field-value">${formatFieldValue(value)}</div>
1392
+ </div>
1393
+ `
1394
+ ).join("");
1395
+ return {
1396
+ metadataLogo,
1397
+ metadataName,
1398
+ userAvatar,
1399
+ userName,
1400
+ origin,
1401
+ isVerifiedOrigin,
1402
+ primaryType: typedData.primaryType,
1403
+ messageFields,
1404
+ domainFields,
1405
+ fullMessageJson: JSON.stringify(typedData.message, null, 2)
1406
+ };
1407
+ }
1408
+
1409
+ // src/iframe/templates/data/confirm-tx-template-data.ts
1410
+ function formatAddress(address) {
1411
+ if (address.length < 10) return address;
1412
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
1413
+ }
1414
+ function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile) {
1415
+ const isVerifiedOrigin = project.domains.includes(origin);
1416
+ const isUserOp = !!transaction.userOpDetails;
1417
+ const displayName = metadata?.name || project.name;
1418
+ const displayLogo = metadata?.logo || project.logoUrl || "";
1419
+ const userAvatar = userProfile?.avatar || "";
1420
+ const userName = userProfile?.displayName || "Lumia Passport";
1421
+ const domainStatusClass = isVerifiedOrigin ? "verified" : "unverified";
1422
+ const domainStatusText = isVerifiedOrigin ? "\u2705" : "\u26A0\uFE0F";
1423
+ const fromAddress = formatAddress(transaction.userOpDetails?.sender || "");
1424
+ const toAddress = formatAddress(transaction.userOpDetails?.callTarget || transaction.to || "");
1425
+ const formatValue = (valueWei) => {
1426
+ try {
1427
+ const value = BigInt(valueWei);
1428
+ if (value === 0n) return "0";
1429
+ const tokens = Number(value) / 1e18;
1430
+ if (tokens >= 1) {
1431
+ return tokens.toFixed(4).replace(/\.?0+$/, "");
1432
+ } else {
1433
+ return tokens.toFixed(8).replace(/\.?0+$/, "");
1434
+ }
1435
+ } catch {
1436
+ return "0";
1437
+ }
1438
+ };
1439
+ const transactionValue = transaction.value || "0";
1440
+ const formattedValue = formatValue(transactionValue);
1441
+ const hasValue = BigInt(transactionValue) > 0n;
1442
+ const showContractInteraction = isUserOp && !!transaction.userOpDetails?.callData && transaction.userOpDetails.callData !== "0x";
1443
+ const isSponsored = isUserOp && !!transaction.userOpDetails?.paymaster;
1444
+ const estimatedCost = isSponsored ? "0 (Sponsored)" : "Free (Sponsored Transaction)";
1445
+ const showHighRisk = risk.level !== "LOW";
1446
+ const riskLevel = risk.level;
1447
+ const riskReasons = risk.reasons.map((r) => `<div>\u2022 ${r}</div>`).join("");
1448
+ return {
1449
+ displayName,
1450
+ displayLogo,
1451
+ userAvatar,
1452
+ userName,
1453
+ origin,
1454
+ domainStatusClass,
1455
+ domainStatusText,
1456
+ fromAddress,
1457
+ toAddress,
1458
+ hasValue,
1459
+ formattedValue,
1460
+ showContractInteraction,
1461
+ estimatedCost,
1462
+ isSponsored,
1463
+ showHighRisk,
1464
+ riskLevel,
1465
+ riskReasons
1466
+ };
1467
+ }
1468
+
1350
1469
  // src/iframe/lib/authorization-manager.ts
1351
1470
  var AuthorizationManager = class {
1352
1471
  constructor() {
@@ -1504,7 +1623,6 @@ var AuthorizationManager = class {
1504
1623
  */
1505
1624
  createAuthorizationModal(project, origin, metadata, userProfile) {
1506
1625
  const modal = document.createElement("div");
1507
- modal.className = "authorization-modal";
1508
1626
  const templateData = buildAuthorizationTemplateData(project, origin, metadata, userProfile);
1509
1627
  const template = TemplateLoader.getTemplate("authorization");
1510
1628
  const populatedHTML = TemplateEngine.render(template, templateData);
@@ -3547,8 +3665,8 @@ var TrustedAppsManager = class {
3547
3665
  /**
3548
3666
  * Add app to trusted list
3549
3667
  */
3550
- addTrustedApp(userId, projectId, origin) {
3551
- console.log("[TrustedApps] Adding trusted app:", { userId, projectId, origin });
3668
+ addTrustedApp(userId, projectId, origin, appName, appLogo) {
3669
+ console.log("[TrustedApps] Adding trusted app:", { userId, projectId, origin, appName });
3552
3670
  const trustedApps = this.getTrustedApps();
3553
3671
  const exists = trustedApps.some(
3554
3672
  (app) => app.userId === userId && app.projectId === projectId && app.origin === origin
@@ -3561,7 +3679,9 @@ var TrustedAppsManager = class {
3561
3679
  userId,
3562
3680
  projectId,
3563
3681
  origin,
3564
- trustedAt: Date.now()
3682
+ trustedAt: Date.now(),
3683
+ appName,
3684
+ appLogo
3565
3685
  });
3566
3686
  this.saveTrustedApps(trustedApps);
3567
3687
  console.log("[TrustedApps] \u2705 App added to trusted list");
@@ -3749,7 +3869,7 @@ var SigningManager = class extends TokenRefreshApiClient {
3749
3869
  throw new Error("User rejected transaction");
3750
3870
  }
3751
3871
  if (confirmResult.trustApp) {
3752
- this.trustedApps.addTrustedApp(userId, projectId, origin);
3872
+ this.trustedApps.addTrustedApp(userId, projectId, origin, confirmResult.appName, confirmResult.appLogo);
3753
3873
  }
3754
3874
  } else if (isMinimalOperation) {
3755
3875
  console.log("[iframe][Sign] Auto-approving minimal safe operation (account deployment)");
@@ -3869,7 +3989,7 @@ var SigningManager = class extends TokenRefreshApiClient {
3869
3989
  const trustApp = trustCheckbox?.checked || false;
3870
3990
  modal.remove();
3871
3991
  this.hideIframe();
3872
- resolve({ confirmed: true, trustApp });
3992
+ resolve({ confirmed: true, trustApp, appName: metadata?.name, appLogo: metadata?.logo });
3873
3993
  }
3874
3994
  });
3875
3995
  cancelBtn?.addEventListener("click", () => {
@@ -3886,135 +4006,9 @@ var SigningManager = class extends TokenRefreshApiClient {
3886
4006
  createConfirmationModal(userId, projectId, project, origin, transaction, risk, metadata, userProfile) {
3887
4007
  const modal = document.createElement("div");
3888
4008
  modal.className = "transaction-confirmation-modal";
3889
- const isVerifiedOrigin = project.domains.includes(origin);
3890
- const isUserOp = !!transaction.userOpDetails;
3891
- const userAvatar = userProfile?.avatar;
3892
- const userName = userProfile?.displayName || "Lumia Passport";
3893
- const isSponsored = isUserOp && !!transaction.userOpDetails?.paymaster;
3894
- const estimatedCost = isSponsored ? "0 (Sponsored)" : "Free (Sponsored Transaction)";
3895
- const displayName = metadata?.name || project.name;
3896
- const displayLogo = metadata?.logo || project.logoUrl;
3897
- const fromAddress = transaction.userOpDetails?.sender;
3898
- const toAddress = transaction.userOpDetails?.callTarget || transaction.to;
3899
- const formatValue = (valueWei) => {
3900
- try {
3901
- const value = BigInt(valueWei);
3902
- if (value === 0n) return "0";
3903
- const tokens = Number(value) / 1e18;
3904
- if (tokens >= 1) {
3905
- return tokens.toFixed(4).replace(/\.?0+$/, "");
3906
- } else {
3907
- return tokens.toFixed(8).replace(/\.?0+$/, "");
3908
- }
3909
- } catch {
3910
- return "0";
3911
- }
3912
- };
3913
- const transactionValue = transaction.value || "0";
3914
- const formattedValue = formatValue(transactionValue);
3915
- const hasValue = BigInt(transactionValue) > 0n;
3916
- modal.innerHTML = `
3917
- <div class="modal-overlay">
3918
- <div class="modal-content">
3919
- <!-- Header with logos -->
3920
- <div class="auth-header">
3921
- <div class="logo-container">
3922
- ${displayLogo ? `<img src="${displayLogo}" alt="${displayName}" class="app-logo" />` : '<div class="app-logo-placeholder"></div>'}
3923
- <div class="logo-connector">
3924
- <svg width="24" height="16" viewBox="0 0 24 16" fill="currentColor">
3925
- <path d="M23.7071 8.70711C24.0976 8.31658 24.0976 7.68342 23.7071 7.29289L17.3431 0.928932C16.9526 0.538408 16.3195 0.538408 15.9289 0.928932C15.5384 1.31946 15.5384 1.95262 15.9289 2.34315L21.5858 8L15.9289 13.6569C15.5384 14.0474 15.5384 14.6805 15.9289 15.0711C16.3195 15.4616 16.9526 15.4616 17.3431 15.0711L23.7071 8.70711ZM0 9H23V7H0V9Z"/>
3926
- </svg>
3927
- </div>
3928
- ${userAvatar ? `<img src="${userAvatar}" alt="${userName}" class="lumia-logo user-avatar" />` : '<img src="./lumia-logo.svg" alt="Lumia Passport" class="lumia-logo" />'}
3929
- </div>
3930
- </div>
3931
-
3932
- <!-- Transaction title -->
3933
- <h2 class="auth-title">Confirm Transaction</h2>
3934
-
3935
- <!-- Application info -->
3936
- <div class="auth-description">
3937
- <div class="domain-info ${isVerifiedOrigin ? "verified" : "unverified"}">
3938
- <span class="domain-label">
3939
- ${isVerifiedOrigin ? "\u2713 Verified:" : "\u26A0\uFE0F Unverified:"}
3940
- </span>
3941
- <span class="domain-value">${origin}</span>
3942
- </div>
3943
- </div>
3944
-
3945
- <!-- Transaction details box -->
3946
- <div class="permissions-box">
3947
- ${fromAddress ? `
3948
- <div class="detail-row" style="margin-bottom: 0.5rem;">
3949
- <div style="color: var(--iframe-text-secondary); font-size: 0.875rem; margin-bottom: 0.25rem;"><strong>From:</strong></div>
3950
- <code style="font-size: 0.75rem; word-break: break-all; display: block; color: var(--iframe-text);">${fromAddress}</code>
3951
- </div>
3952
- ` : ""}
3953
-
3954
- ${toAddress ? `
3955
- <div class="detail-row" style="margin-bottom: 0.5rem;">
3956
- <div style="color: var(--iframe-text-secondary); font-size: 0.875rem; margin-bottom: 0.25rem;"><strong>To:</strong></div>
3957
- <code style="font-size: 0.75rem; word-break: break-all; display: block; color: var(--iframe-text);">${toAddress}</code>
3958
- </div>
3959
- ` : ""}
3960
-
3961
- ${hasValue ? `
3962
- <div class="detail-row" style="margin-bottom: 0.5rem;">
3963
- <span style="color: var(--iframe-text-secondary);"><strong>Amount:</strong></span>
3964
- <strong style="color: var(--iframe-text); font-size: 1rem;">${formattedValue} LUMIA</strong>
3965
- </div>
3966
- ` : ""}
3967
-
3968
- ${isUserOp && transaction.userOpDetails?.callData && transaction.userOpDetails.callData !== "0x" ? `
3969
- <div class="detail-row" style="margin-bottom: 0.5rem;">
3970
- <span style="color: var(--iframe-text-secondary);"><strong>Action:</strong></span>
3971
- <span style="color: var(--iframe-text);">Contract Interaction</span>
3972
- </div>
3973
- ` : ""}
3974
-
3975
- <div class="detail-row" style="margin-bottom: 0.5rem;">
3976
- <span style="color: var(--iframe-text-secondary);"><strong>Estimated Cost:</strong></span>
3977
- <strong style="color: #10b981;">${estimatedCost}</strong>
3978
- </div>
3979
-
3980
- ${isSponsored ? `
3981
- <div class="detail-row" style="background: #d1fae5; border-radius: 6px; padding: 0.75rem; margin-top: 0.5rem;">
3982
- <span style="color: #047857;">\u2713 Gas fees will be paid by the application (no cost to you)</span>
3983
- </div>
3984
- ` : ""}
3985
- </div>
3986
-
3987
- ${risk.level !== "LOW" ? `
3988
- <div class="security-warning">
3989
- <strong>\u26A0\uFE0F ${risk.level} RISK</strong>
3990
- ${risk.reasons.map((r) => `<div>\u2022 ${r}</div>`).join("")}
3991
- </div>
3992
- ` : `
3993
- <div class="security-warning">
3994
- <strong>\u26A0\uFE0F Warning:</strong> Please verify this transaction carefully before confirming.
3995
- </div>
3996
- `}
3997
-
3998
- <div class="trust-app-section">
3999
- <label class="trust-app-label">
4000
- <input type="checkbox" class="trust-app-checkbox" />
4001
- <span>Trust this application and skip confirmation for future transactions</span>
4002
- </label>
4003
- </div>
4004
-
4005
- <!-- Action buttons -->
4006
- <div class="actions">
4007
- <button class="cancel-btn">Reject</button>
4008
- <button class="confirm-btn">Confirm</button>
4009
- </div>
4010
-
4011
- <!-- Footer notice -->
4012
- <div class="auth-footer">
4013
- <p class="footer-note">You can manage trusted applications in your Lumia Passport settings.</p>
4014
- </div>
4015
- </div>
4016
- </div>
4017
- `;
4009
+ const templateData = buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile);
4010
+ const template = TemplateLoader.getTemplate("confirm-tx");
4011
+ modal.innerHTML = TemplateEngine.render(template, templateData);
4018
4012
  return modal;
4019
4013
  }
4020
4014
  /**
@@ -4123,7 +4117,7 @@ var SigningManager = class extends TokenRefreshApiClient {
4123
4117
  throw new Error("User rejected signature request");
4124
4118
  }
4125
4119
  if (confirmResult.trustApp) {
4126
- this.trustedApps.addTrustedApp(userId, projectId, origin);
4120
+ this.trustedApps.addTrustedApp(userId, projectId, origin, confirmResult.appName, confirmResult.appLogo);
4127
4121
  }
4128
4122
  }
4129
4123
  const keyshareData = this.storage.loadKeyshare(userId);
@@ -4158,7 +4152,7 @@ var SigningManager = class extends TokenRefreshApiClient {
4158
4152
  const trustApp = trustCheckbox?.checked || false;
4159
4153
  modal.remove();
4160
4154
  this.hideIframe();
4161
- resolve({ confirmed: true, trustApp });
4155
+ resolve({ confirmed: true, trustApp, appName: metadata?.name, appLogo: metadata?.logo });
4162
4156
  }
4163
4157
  });
4164
4158
  cancelBtn?.addEventListener("click", () => {
@@ -4174,97 +4168,9 @@ var SigningManager = class extends TokenRefreshApiClient {
4174
4168
  */
4175
4169
  createEIP712ConfirmationModal(userId, projectId, project, origin, typedData, metadata, userProfile) {
4176
4170
  const modal = document.createElement("div");
4177
- modal.className = "eip712-confirmation-modal";
4178
- const isVerifiedOrigin = project.domains.includes(origin);
4179
- const userAvatar = userProfile?.avatar;
4180
- const userName = userProfile?.displayName || "Lumia Passport";
4181
- const formatFieldValue = (value) => {
4182
- if (Array.isArray(value)) {
4183
- return `[${value.map((v) => formatFieldValue(v)).join(", ")}]`;
4184
- }
4185
- if (typeof value === "bigint") {
4186
- return value.toString();
4187
- }
4188
- if (typeof value === "object" && value !== null) {
4189
- return JSON.stringify(value, null, 2);
4190
- }
4191
- return String(value);
4192
- };
4193
- const messageFields = Object.entries(typedData.message).map(
4194
- ([key, value]) => `
4195
- <div class="eip712-field">
4196
- <div class="field-name">${key}:</div>
4197
- <div class="field-value">${formatFieldValue(value)}</div>
4198
- </div>
4199
- `
4200
- ).join("");
4201
- const domainFields = Object.entries(typedData.domain).filter(([_, value]) => value !== void 0).map(
4202
- ([key, value]) => `
4203
- <div class="eip712-field">
4204
- <div class="field-name">${key}:</div>
4205
- <div class="field-value">${formatFieldValue(value)}</div>
4206
- </div>
4207
- `
4208
- ).join("");
4209
- modal.innerHTML = `
4210
- <div class="modal-overlay">
4211
- <div class="modal-content eip712-modal">
4212
- <!-- Header -->
4213
- <div class="auth-header">
4214
- <div class="logo-container">
4215
- ${metadata?.logo ? `<img src="${metadata.logo}" alt="${metadata.name}" class="project-logo" />` : '<div class="project-logo-placeholder">\u{1F510}</div>'}
4216
- <span class="arrow-icon">\u2192</span>
4217
- ${userAvatar ? `<img src="${userAvatar}" alt="${userName}" class="lumia-logo user-avatar" style="width: 40px; height: 40px;" />` : '<div class="lumia-logo">L</div>'}
4218
- </div>
4219
- <h2 class="modal-title">Signature Request</h2>
4220
- <p class="origin-text">
4221
- ${isVerifiedOrigin ? '<span class="verified-badge">\u2713</span>' : ""}
4222
- ${origin}
4223
- </p>
4224
- </div>
4225
-
4226
- <!-- EIP712 Message Content -->
4227
- <div class="eip712-content">
4228
- <details class="eip712-details">
4229
- <summary>Advanced details</summary>
4230
-
4231
- <div class="section-title">\u{1F4DD} Message</div>
4232
- <div class="eip712-section">
4233
- <div class="section-subtitle">${typedData.primaryType}</div>
4234
- ${messageFields}
4235
- </div>
4236
-
4237
- <div class="section-title" style="margin-top: 16px;">\u{1F50D} Domain</div>
4238
- <div class="eip712-section">
4239
- ${domainFields}
4240
- </div>
4241
-
4242
- <div class="section-title" style="margin-top: 16px;">\u{1F4CB} Full Message</div>
4243
- <pre class="eip712-raw"><code>${JSON.stringify(typedData.message, null, 2)}</code></pre>
4244
- </details>
4245
- </div>
4246
-
4247
- <!-- Trust App Option -->
4248
- <div class="trust-app-section">
4249
- <label class="trust-app-label">
4250
- <input type="checkbox" class="trust-app-checkbox" />
4251
- <span>Trust this application and skip confirmation for future signatures</span>
4252
- </label>
4253
- </div>
4254
-
4255
- <!-- Action Buttons -->
4256
- <div class="actions">
4257
- <button class="cancel-btn">Reject</button>
4258
- <button class="confirm-btn">Sign</button>
4259
- </div>
4260
-
4261
- <!-- Footer Notice -->
4262
- <div class="footer-notice">
4263
- <p class="footer-note">Only sign messages from applications you trust.</p>
4264
- </div>
4265
- </div>
4266
- </div>
4267
- `;
4171
+ const templateData = buildSignTxTemplateData(project, origin, typedData, metadata, userProfile);
4172
+ const template = TemplateLoader.getTemplate("sign-eip712-tx");
4173
+ modal.innerHTML = TemplateEngine.render(template, templateData);
4268
4174
  return modal;
4269
4175
  }
4270
4176
  /**
@@ -4286,7 +4192,7 @@ var SigningManager = class extends TokenRefreshApiClient {
4286
4192
  };
4287
4193
 
4288
4194
  // src/iframe/main.ts
4289
- var IFRAME_VERSION = "1.14.8";
4195
+ var IFRAME_VERSION = "1.14.10";
4290
4196
  var IframeWallet = class {
4291
4197
  constructor() {
4292
4198
  console.log("=".repeat(60));
@@ -4939,7 +4845,7 @@ var IframeWallet = class {
4939
4845
  }
4940
4846
  };
4941
4847
  function toggleAuthPermissions(e) {
4942
- const isToggler = e.target.id === "data-auth-permissions-toggler";
4848
+ const isToggler = e.target?.id === "data-auth-permissions-toggler";
4943
4849
  if (!isToggler) return;
4944
4850
  const details = document.getElementById("data-auth-permissions-block");
4945
4851
  const isOpen = details.hasAttribute("aria-expanded") && details.getAttribute("aria-expanded") === "true";
@@ -4954,6 +4860,22 @@ function toggleAuthPermissions(e) {
4954
4860
  toggler.innerHTML = "Hide Permissions \u25B2";
4955
4861
  }
4956
4862
  }
4863
+ function toggleConfirmTransactionDetails(e) {
4864
+ const isToggler = e.target?.id === "confirm-transaction-details-toggler";
4865
+ if (!isToggler) return;
4866
+ const details = document.getElementById("confirm-transaction-details-block");
4867
+ const isOpen = details.hasAttribute("aria-expanded") && details.getAttribute("aria-expanded") === "true";
4868
+ const toggler = details.querySelector("#confirm-transaction-details-toggler");
4869
+ if (isOpen) {
4870
+ details.setAttribute("aria-expanded", "false");
4871
+ details.style.setProperty("--confirm-transaction-details-h", "0px");
4872
+ toggler.innerHTML = "Show Transaction Details \u25BC";
4873
+ } else {
4874
+ details.setAttribute("aria-expanded", "true");
4875
+ details.style.setProperty("--confirm-transaction-details-h", "200px");
4876
+ toggler.innerHTML = "Hide Transaction Details \u25B2";
4877
+ }
4878
+ }
4957
4879
  function initWallet() {
4958
4880
  const wallet = new IframeWallet();
4959
4881
  wallet.initialize().catch((error) => {
@@ -4969,7 +4891,10 @@ function initWallet() {
4969
4891
  `;
4970
4892
  }
4971
4893
  });
4972
- document.addEventListener("click", toggleAuthPermissions);
4894
+ document.addEventListener("click", (e) => {
4895
+ toggleAuthPermissions(e);
4896
+ toggleConfirmTransactionDetails(e);
4897
+ });
4973
4898
  }
4974
4899
  if (document.readyState === "loading") {
4975
4900
  document.addEventListener("DOMContentLoaded", initWallet);