diff --git a/modules/rwallet/components/folk-wallet-viewer.ts b/modules/rwallet/components/folk-wallet-viewer.ts index 2a32e90..2bafe8c 100644 --- a/modules/rwallet/components/folk-wallet-viewer.ts +++ b/modules/rwallet/components/folk-wallet-viewer.ts @@ -101,6 +101,7 @@ class FolkWalletViewer extends HTMLElement { // Linked wallets state private isAuthenticated = false; private passKeyEOA = ""; + private userDID = ""; private linkedWallets: LinkedWallet[] = []; private showProviderPicker = false; private discoveredProviders: DiscoveredProvider[] = []; @@ -193,6 +194,7 @@ class FolkWalletViewer extends HTMLElement { this.isAuthenticated = true; this.topTab = "my-wallets"; this.passKeyEOA = parsed.claims?.eid?.walletAddress || ""; + this.userDID = parsed.claims?.did || ""; this.loadLinkedWallets().then(() => this.loadMyWalletBalances()); this.loadCRDTBalances(); } @@ -1235,6 +1237,24 @@ class FolkWalletViewer extends HTMLElement { font-size: 13px; color: var(--rs-text-primary); } + /* ── EncryptID identity card ── */ + .encryptid-card { border-color: rgba(168,85,247,0.25); } + .encryptid-wallets { display: flex; flex-direction: column; gap: 12px; } + .encryptid-wallet-section { + padding: 10px 12px; border-radius: 8px; + background: var(--rs-bg-hover); border: 1px solid var(--rs-border-subtle); + } + .encryptid-wallet-label { + display: flex; align-items: center; gap: 6px; + font-size: 12px; font-weight: 600; text-transform: uppercase; + letter-spacing: 0.5px; color: var(--rs-text-secondary); margin-bottom: 8px; + } + .wallet-type-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } + .wallet-type-addr { + font-family: monospace; font-weight: 400; font-size: 11px; + color: var(--rs-text-muted); margin-left: auto; text-transform: none; letter-spacing: 0; + } + /* ── Aggregate stats ── */ .aggregate-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); @@ -1421,20 +1441,15 @@ class FolkWalletViewer extends HTMLElement { let html = '
'; - // EncryptID wallet card - if (this.passKeyEOA) { - html += this.renderWalletCard(this.passKeyEOA, "EncryptID", "encryptid", true); - } + // EncryptID identity card — always shown for authenticated users + // Every EncryptID identity has both a CRDT wallet (DID) and an EVM wallet (passkey-derived) + html += this.renderEncryptIdCard(); - // Linked wallet cards + // Linked wallet cards (external wallets) for (const w of this.linkedWallets) { html += this.renderWalletCard(w.address, w.providerName || w.type.toUpperCase(), w.type, false, w.id); } - if (!this.passKeyEOA && this.linkedWallets.length === 0) { - html += '
No wallets linked yet. Link a browser wallet to get started.
'; - } - html += '
'; // Aggregate total @@ -1450,7 +1465,104 @@ class FolkWalletViewer extends HTMLElement { return html; } - private renderWalletCard(address: string, label: string, badgeClass: string, isEncryptId: boolean, walletId?: string): string { + private renderEncryptIdCard(): string { + // ── CRDT Wallet (DID) ── + const didShort = this.userDID + ? `${this.userDID.slice(0, 16)}...${this.userDID.slice(-6)}` + : "loading..."; + + let crdtRows = ""; + if (this.crdtLoading) { + crdtRows = `
Loading...
`; + } else if (this.crdtBalances.length > 0) { + crdtRows = this.crdtBalances.map(t => { + const formatted = (t.balance / Math.pow(10, t.decimals)).toFixed(2); + return `
+ ${t.icon || '\u{1FA99}'} + ${this.esc(t.symbol)} + ${this.esc(t.name)} + ${formatted} +
`; + }).join(""); + } else { + crdtRows = `
No CRDT token balances
`; + } + + // ── EVM Wallet (passkey-derived) ── + const chainBalances = this.passKeyEOA + ? (this.myWalletBalances.get(this.passKeyEOA.toLowerCase()) || []) + : []; + + const allBals: Array = []; + for (const ch of chainBalances) { + for (const b of ch.balances) { + allBals.push({ ...b, chainId: ch.chainId, chainName: ch.chainName }); + } + } + + const sorted = allBals + .filter(b => parseFloat(b.fiatBalance || "0") > 0.01 || BigInt(b.balance || "0") > 0n) + .sort((a, b) => parseFloat(b.fiatBalance || "0") - parseFloat(a.fiatBalance || "0")); + + const totalUSD = sorted.reduce((sum, b) => sum + parseFloat(b.fiatBalance || "0"), 0); + + let evmBalanceRows = ""; + if (sorted.length > 0) { + evmBalanceRows = sorted.slice(0, 10).map(b => { + const color = CHAIN_COLORS[b.chainId] || "#888"; + return ` +
${this.esc(b.chainName)}
+ ${this.esc(b.token?.symbol || "ETH")} + ${this.formatBalance(b.balance, b.token?.decimals || 18)} + ${this.formatUSD(b.fiatBalance)} + `; + }).join(""); + if (sorted.length > 10) { + evmBalanceRows += `+ ${sorted.length - 10} more tokens`; + } + } + + return ` +
+
+
+ EncryptID + ${this.formatUSD(String(totalUSD))} +
+ ${this.passKeyEOA ? `` : ""} +
+ +
+ +
+
+ + CRDT Wallet + ${this.esc(didShort)} +
+ ${crdtRows} +
+ + +
+
+ + EVM Wallet + ${this.passKeyEOA ? this.shortenAddress(this.passKeyEOA) : "not derived"} +
+ ${evmBalanceRows ? ` + + + + + ${evmBalanceRows} +
ChainTokenBalanceUSD
` : `
No on-chain balances found
`} +
+
+
`; + } + + private renderWalletCard(address: string, label: string, badgeClass: string, _isEncryptId: boolean, walletId?: string): string { const chainBalances = this.myWalletBalances.get(address.toLowerCase()) || []; // Flatten all balances for this wallet @@ -1483,24 +1595,6 @@ class FolkWalletViewer extends HTMLElement { } } - // CRDT tokens for EncryptID wallet - let crdtSection = ""; - if (isEncryptId && this.crdtBalances.length > 0) { - const crdtRows = this.crdtBalances.map(t => { - const formatted = (t.balance / Math.pow(10, t.decimals)).toFixed(2); - return `
- ${t.icon || '\u{1FA99}'} - ${this.esc(t.symbol)} - ${formatted} -
`; - }).join(""); - crdtSection = ` -
-
Local Tokens (CRDT)
- ${crdtRows} -
`; - } - return `
@@ -1521,7 +1615,6 @@ class FolkWalletViewer extends HTMLElement { ${balanceRows} ` : `
No on-chain balances found
`} - ${crdtSection}
`; } diff --git a/modules/rwallet/mod.ts b/modules/rwallet/mod.ts index 6cf9633..16405ea 100644 --- a/modules/rwallet/mod.ts +++ b/modules/rwallet/mod.ts @@ -840,7 +840,7 @@ function renderWallet(spaceSlug: string, initialView?: string) { modules: getModuleInfoList(), theme: "dark", body: ``, - scripts: ``, + scripts: ``, styles: ``, }); }