@lumiapassport/ui-kit 1.14.14 → 1.14.16

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.14</title>
18
+ <title>Lumia Passport Secure Wallet - iframe version 1.14.16</title>
19
19
 
20
20
  <!-- Styles will be injected by build process -->
21
21
  <style>
@@ -1270,7 +1270,7 @@ var TemplateEngine = class {
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: 0; justify-content: center">\n <!-- 10px overlap -->\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\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 <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\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 </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
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: 0; justify-content: center">\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\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 <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\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 </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';
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: 0; justify-content: center">\n <div style="width: 38px; height: 48px; z-index: 10; overflow: visible">\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 <!-- ------------------------------------------------------------ -->\n\n <div style="width: 38px; height: 48px; z-index: 5; overflow: visible">\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 </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 <a\n href="{{fromAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{fromAddressFull}}"\n >\n <code>{{fromAddress}}</code>\n </a>\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 <a\n href="{{toAddressExplorerUrl}}"\n target="_blank"\n rel="noopener noreferrer"\n style="word-break: break-all; display: block; color: #3b82f6; text-decoration: none"\n title="{{toAddressFull}}"\n >\n <code>{{toAddress}}</code>\n </a>\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">{{actionDescription}}</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
1274
 
1275
1275
  // src/iframe/templates/html/ready-indicator.html
1276
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';
@@ -1407,11 +1407,52 @@ function buildSignTxTemplateData(project, origin, typedData, metadata, userProfi
1407
1407
  }
1408
1408
 
1409
1409
  // src/iframe/templates/data/confirm-tx-template-data.ts
1410
+ var ERC20_APPROVE_SELECTOR = "0x095ea7b3";
1411
+ var DEFAULT_EXPLORER_URL = "https://beam-explorer.lumia.org";
1410
1412
  function formatAddress(address) {
1411
1413
  if (address.length < 10) return address;
1412
1414
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
1413
1415
  }
1414
- function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile) {
1416
+ function getAddressExplorerUrl(address, explorerUrl) {
1417
+ if (!address) return "";
1418
+ const baseUrl = explorerUrl.replace(/\/$/, "");
1419
+ return `${baseUrl}/address/${address}`;
1420
+ }
1421
+ function parseERC20Approve(callData) {
1422
+ if (!callData || callData.length < 10) return null;
1423
+ const selector = callData.slice(0, 10).toLowerCase();
1424
+ if (selector !== ERC20_APPROVE_SELECTOR) return null;
1425
+ if (callData.length < 138) return null;
1426
+ try {
1427
+ const spender = "0x" + callData.slice(34, 74);
1428
+ const amountHex = callData.slice(74, 138);
1429
+ const amount = BigInt("0x" + amountHex);
1430
+ return { spender, amount };
1431
+ } catch {
1432
+ return null;
1433
+ }
1434
+ }
1435
+ function formatApproveAmount(amount) {
1436
+ const MAX_UINT256 = 2n ** 256n - 1n;
1437
+ const NEAR_MAX = 2n ** 255n;
1438
+ if (amount >= NEAR_MAX) {
1439
+ return "Unlimited";
1440
+ }
1441
+ const tokens = Number(amount) / 1e18;
1442
+ if (tokens >= 1e9) {
1443
+ return `${(tokens / 1e9).toFixed(2)}B`;
1444
+ } else if (tokens >= 1e6) {
1445
+ return `${(tokens / 1e6).toFixed(2)}M`;
1446
+ } else if (tokens >= 1e3) {
1447
+ return `${(tokens / 1e3).toFixed(2)}K`;
1448
+ } else if (tokens >= 1) {
1449
+ return tokens.toFixed(4).replace(/\.?0+$/, "");
1450
+ } else if (tokens > 0) {
1451
+ return tokens.toFixed(8).replace(/\.?0+$/, "");
1452
+ }
1453
+ return "0";
1454
+ }
1455
+ function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile, explorerUrl) {
1415
1456
  const isVerifiedOrigin = project.domains.includes(origin);
1416
1457
  const isUserOp = !!transaction.userOpDetails;
1417
1458
  const displayName = metadata?.name || project.name;
@@ -1420,8 +1461,13 @@ function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata
1420
1461
  const userName = userProfile?.displayName || "Lumia Passport";
1421
1462
  const domainStatusClass = isVerifiedOrigin ? "verified" : "unverified";
1422
1463
  const domainStatusText = isVerifiedOrigin ? "\u2705" : "\u26A0\uFE0F";
1423
- const fromAddress = formatAddress(transaction.userOpDetails?.sender || "");
1424
- const toAddress = formatAddress(transaction.userOpDetails?.callTarget || transaction.to || "");
1464
+ const fromAddressFull = transaction.userOpDetails?.sender || "";
1465
+ const toAddressFull = transaction.userOpDetails?.callTarget || transaction.to || "";
1466
+ const fromAddress = formatAddress(fromAddressFull);
1467
+ const toAddress = formatAddress(toAddressFull);
1468
+ const baseExplorerUrl = explorerUrl || DEFAULT_EXPLORER_URL;
1469
+ const fromAddressExplorerUrl = getAddressExplorerUrl(fromAddressFull, baseExplorerUrl);
1470
+ const toAddressExplorerUrl = getAddressExplorerUrl(toAddressFull, baseExplorerUrl);
1425
1471
  const formatValue = (valueWei) => {
1426
1472
  try {
1427
1473
  const value = BigInt(valueWei);
@@ -1439,7 +1485,16 @@ function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata
1439
1485
  const transactionValue = transaction.value || "0";
1440
1486
  const formattedValue = formatValue(transactionValue);
1441
1487
  const hasValue = BigInt(transactionValue) > 0n;
1442
- const showContractInteraction = isUserOp && !!transaction.userOpDetails?.callData && transaction.userOpDetails.callData !== "0x";
1488
+ const callData = transaction.userOpDetails?.callData || transaction.data || "";
1489
+ const showContractInteraction = isUserOp && !!callData && callData !== "0x";
1490
+ let actionDescription = "Contract Interaction";
1491
+ if (showContractInteraction) {
1492
+ const approveData = parseERC20Approve(callData);
1493
+ if (approveData) {
1494
+ const formattedAmount = formatApproveAmount(approveData.amount);
1495
+ actionDescription = `Approve ${formattedAmount} tokens`;
1496
+ }
1497
+ }
1443
1498
  const isSponsored = isUserOp && !!transaction.userOpDetails?.paymaster;
1444
1499
  const estimatedCost = isSponsored ? "0 (Sponsored)" : "Free (Sponsored Transaction)";
1445
1500
  const showHighRisk = risk.level !== "LOW";
@@ -1455,9 +1510,14 @@ function buildConfirmTxTemplateData(project, origin, transaction, risk, metadata
1455
1510
  domainStatusText,
1456
1511
  fromAddress,
1457
1512
  toAddress,
1513
+ fromAddressFull,
1514
+ toAddressFull,
1515
+ fromAddressExplorerUrl,
1516
+ toAddressExplorerUrl,
1458
1517
  hasValue,
1459
1518
  formattedValue,
1460
1519
  showContractInteraction,
1520
+ actionDescription,
1461
1521
  estimatedCost,
1462
1522
  isSponsored,
1463
1523
  showHighRisk,
@@ -1476,6 +1536,13 @@ var AuthorizationManager = class {
1476
1536
  this.metadataCache = /* @__PURE__ */ new Map();
1477
1537
  console.log("[iframe][Auth] Initialized");
1478
1538
  }
1539
+ /**
1540
+ * Check if project is a trusted authorized app (auto-skip auth dialog)
1541
+ */
1542
+ async isTrustedAuthorizedApp(projectId) {
1543
+ const metadata = await this.fetchProjectMetadata(projectId);
1544
+ return metadata?.trustedAuthorizedApp === true;
1545
+ }
1479
1546
  /**
1480
1547
  * Fetch project metadata from public API (with caching)
1481
1548
  */
@@ -4035,7 +4102,7 @@ var SigningManager = class extends TokenRefreshApiClient {
4035
4102
  createConfirmationModal(userId, projectId, project, origin, transaction, risk, metadata, userProfile) {
4036
4103
  const modal = document.createElement("div");
4037
4104
  modal.className = "transaction-confirmation-modal";
4038
- const templateData = buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile);
4105
+ const templateData = buildConfirmTxTemplateData(project, origin, transaction, risk, metadata, userProfile, this.explorerUrl);
4039
4106
  const template = TemplateLoader.getTemplate("confirm-tx");
4040
4107
  modal.innerHTML = TemplateEngine.render(template, templateData);
4041
4108
  return modal;
@@ -4221,7 +4288,7 @@ var SigningManager = class extends TokenRefreshApiClient {
4221
4288
  };
4222
4289
 
4223
4290
  // src/iframe/main.ts
4224
- var IFRAME_VERSION = "1.14.14";
4291
+ var IFRAME_VERSION = "1.14.16";
4225
4292
  var IframeWallet = class {
4226
4293
  constructor() {
4227
4294
  console.log("=".repeat(60));
@@ -4355,20 +4422,27 @@ var IframeWallet = class {
4355
4422
  console.log(`[iframe] AUTHENTICATE: userId=${userId}, projectId=${projectId}`);
4356
4423
  const isAuthorized = await this.authManager.checkAuthorization(userId, projectId);
4357
4424
  if (!isAuthorized) {
4358
- console.log(`[iframe] Authorization needed for project: ${projectId}`);
4359
- const projectInfo = {
4360
- id: projectId,
4361
- name: "Application",
4362
- logoUrl: "",
4363
- website: origin,
4364
- domains: [origin]
4365
- };
4366
- const consent = await this.authManager.requestAuthorization(projectInfo, origin, { avatar, displayName });
4367
- if (!consent) {
4368
- throw new Error("User denied authorization");
4425
+ const isTrustedApp = await this.authManager.isTrustedAuthorizedApp(projectId);
4426
+ if (isTrustedApp) {
4427
+ console.log(`[iframe] Project ${projectId} is a trusted authorized app, auto-authorizing`);
4428
+ await this.authManager.storeAuthorization(userId, projectId, origin);
4429
+ console.log(`[iframe] \u2705 Auto-authorization granted for trusted app: ${userId} -> ${projectId}`);
4430
+ } else {
4431
+ console.log(`[iframe] Authorization needed for project: ${projectId}`);
4432
+ const projectInfo = {
4433
+ id: projectId,
4434
+ name: "Application",
4435
+ logoUrl: "",
4436
+ website: origin,
4437
+ domains: [origin]
4438
+ };
4439
+ const consent = await this.authManager.requestAuthorization(projectInfo, origin, { avatar, displayName });
4440
+ if (!consent) {
4441
+ throw new Error("User denied authorization");
4442
+ }
4443
+ await this.authManager.storeAuthorization(userId, projectId, origin);
4444
+ console.log(`[iframe] \u2705 Authorization granted: ${userId} -> ${projectId}`);
4369
4445
  }
4370
- await this.authManager.storeAuthorization(userId, projectId, origin);
4371
- console.log(`[iframe] \u2705 Authorization granted: ${userId} -> ${projectId}`);
4372
4446
  }
4373
4447
  const address = this.storage.getOwnerAddress(userId);
4374
4448
  this.messenger.sendResponse(