diff --git a/modules/rwallet/components/folk-wallet-viewer.ts b/modules/rwallet/components/folk-wallet-viewer.ts
index 1295d11..38f2812 100644
--- a/modules/rwallet/components/folk-wallet-viewer.ts
+++ b/modules/rwallet/components/folk-wallet-viewer.ts
@@ -800,13 +800,29 @@ class FolkWalletViewer extends HTMLElement {
const incoming: any[] = [];
const outgoing: any[] = [];
const results = data.results || [];
+ const addr = this.address.toLowerCase();
for (const tx of results) {
+ // Outgoing: Safe-initiated multisig transactions
if (tx.txType === "MULTISIG_TRANSACTION") {
outgoing.push(tx);
}
+ // Incoming: external ETH/token transfers to Safe
+ if (tx.txType === "ETHEREUM_TRANSACTION") {
+ if (tx.from && tx.value && tx.value !== "0") {
+ incoming.push({
+ type: "ETHER_TRANSFER",
+ from: tx.from,
+ to: this.address,
+ value: tx.value,
+ executionDate: tx.executionDate,
+ blockTimestamp: tx.executionDate,
+ });
+ }
+ }
+ // Embedded transfers (both tx types may have these)
if (tx.transfers) {
for (const t of tx.transfers) {
- if (t.to?.toLowerCase() === this.address.toLowerCase()) {
+ if (t.to?.toLowerCase() === addr && t.from?.toLowerCase() !== addr) {
incoming.push(t);
}
}
@@ -2606,7 +2622,13 @@ class FolkWalletViewer extends HTMLElement {
}
const sorted = allBals
- .filter(b => parseFloat(b.fiatBalance || "0") > 0.01 || BigInt(b.balance || "0") > 0n)
+ .filter(b => {
+ const fiat = parseFloat(b.fiatBalance || "0");
+ if (fiat > 0.01) return true;
+ if (b.chainId === "local" || b.tokenAddress?.startsWith("crdt:")) return true;
+ if (!b.tokenAddress && BigInt(b.balance || "0") > 0n) return true;
+ return false;
+ })
.sort((a, b) => parseFloat(b.fiatBalance || "0") - parseFloat(a.fiatBalance || "0"));
const totalUSD = sorted.reduce((sum, b) => sum + parseFloat(b.fiatBalance || "0"), 0);
@@ -2679,7 +2701,13 @@ class FolkWalletViewer extends HTMLElement {
}
const sorted = allBals
- .filter(b => parseFloat(b.fiatBalance || "0") > 0.01 || BigInt(b.balance || "0") > 0n)
+ .filter(b => {
+ const fiat = parseFloat(b.fiatBalance || "0");
+ if (fiat > 0.01) return true;
+ if (b.chainId === "local" || b.tokenAddress?.startsWith("crdt:")) return true;
+ if (!b.tokenAddress && BigInt(b.balance || "0") > 0n) return true;
+ return false;
+ })
.sort((a, b) => parseFloat(b.fiatBalance || "0") - parseFloat(a.fiatBalance || "0"));
const totalUSD = sorted.reduce((sum, b) => sum + parseFloat(b.fiatBalance || "0"), 0);
@@ -2732,8 +2760,9 @@ class FolkWalletViewer extends HTMLElement {
for (const ch of chains) {
totalChains.add(ch.chainId);
for (const b of ch.balances) {
- if (parseFloat(b.fiatBalance || "0") > 0.01 || BigInt(b.balance || "0") > 0n) {
- grandTotal += parseFloat(b.fiatBalance || "0");
+ const fiat = parseFloat(b.fiatBalance || "0");
+ if (fiat > 0.01 || (!b.tokenAddress && BigInt(b.balance || "0") > 0n)) {
+ grandTotal += fiat;
totalTokens++;
}
}
@@ -3047,7 +3076,13 @@ class FolkWalletViewer extends HTMLElement {
if (unified.length === 0) return '
No token balances found.
';
const sorted = unified
- .filter((b) => parseFloat(b.fiatBalance || "0") > 0.01 || BigInt(b.balance || "0") > 0n)
+ .filter((b) => {
+ const fiat = parseFloat(b.fiatBalance || "0");
+ if (fiat > 0.01) return true;
+ if (b.chainId === "local" || b.tokenAddress?.startsWith("crdt:")) return true;
+ if (!b.tokenAddress && BigInt(b.balance || "0") > 0n) return true;
+ return false;
+ })
.sort((a, b) => {
const fiatDiff = parseFloat(b.fiatBalance || "0") - parseFloat(a.fiatBalance || "0");
if (fiatDiff !== 0) return fiatDiff;
@@ -3253,7 +3288,13 @@ class FolkWalletViewer extends HTMLElement {
// Aggregate stats across ALL chains (ignoring filter)
const allBalances = this.getUnifiedBalances(true);
const totalUSD = allBalances.reduce((sum, b) => sum + parseFloat(b.fiatBalance || "0"), 0);
- const totalTokens = allBalances.filter((b) => parseFloat(b.fiatBalance || "0") > 0 || BigInt(b.balance || "0") > 0n).length;
+ const totalTokens = allBalances.filter((b) => {
+ const fiat = parseFloat(b.fiatBalance || "0");
+ if (fiat > 0.01) return true;
+ if (b.chainId === "local" || b.tokenAddress?.startsWith("crdt:")) return true;
+ if (!b.tokenAddress && BigInt(b.balance || "0") > 0n) return true;
+ return false;
+ }).length;
// Build chain buttons with "All" filter
const chainButtons = this.detectedChains.map((ch) => {
@@ -3305,8 +3346,6 @@ class FolkWalletViewer extends HTMLElement {
` : ""}
- ${this.renderViewTabs()}
-
${this.activeView === "balances"
? this.renderBalanceTable() + this.renderDefiPositions() + this.renderPaymentActions()
: `
@@ -3500,13 +3539,7 @@ class FolkWalletViewer extends HTMLElement {
this.addToWatchlist(addr, chain, label);
});
- // View tab listeners (skip tour button which has no data-view)
- this.shadow.querySelectorAll(".view-tab[data-view]").forEach((tab) => {
- tab.addEventListener("click", () => {
- const view = (tab as HTMLElement).dataset.view as ViewTab;
- this.handleViewTabClick(view);
- });
- });
+ // View tab listeners no longer needed — shell subnav handles navigation
this.shadow.querySelector("#btn-tour")?.addEventListener("click", () => this.startTour());
diff --git a/modules/rwallet/mod.ts b/modules/rwallet/mod.ts
index c72b684..8dd9fd9 100644
--- a/modules/rwallet/mod.ts
+++ b/modules/rwallet/mod.ts
@@ -47,7 +47,8 @@ routes.get("/api/safe/:chainId/:address/balances", async (c) => {
fiatBalance: item.fiatBalance || "0",
fiatConversion: item.fiatConversion || "0",
}));
- const enriched = await enrichWithPrices(data, chainId);
+ const enriched = (await enrichWithPrices(data, chainId))
+ .filter(b => BigInt(b.balance || "0") > 0n);
c.header("Cache-Control", "public, max-age=30");
return c.json(enriched);
});
@@ -1258,13 +1259,17 @@ function renderWallet(spaceSlug: string, initialView?: string) {
modules: getModuleInfoList(),
theme: "dark",
body: ``,
- scripts: ``,
+ scripts: ``,
styles: ``,
});
}
-routes.get("/wallets", (c) => c.html(renderWallet(c.req.param("space") || "demo", "budget")));
+routes.get("/budget", (c) => c.html(renderWallet(c.req.param("space") || "demo", "budget")));
routes.get("/tokens", (c) => c.html(renderWallet(c.req.param("space") || "demo", "balances")));
+routes.get("/flows", (c) => c.html(renderWallet(c.req.param("space") || "demo", "flows")));
+
+// Legacy aliases
+routes.get("/wallets", (c) => c.html(renderWallet(c.req.param("space") || "demo", "budget")));
routes.get("/transactions", (c) => c.html(renderWallet(c.req.param("space") || "demo", "budget")));
routes.get("/", (c) => c.html(renderWallet(c.req.param("space") || "demo", "budget")));
@@ -1294,8 +1299,8 @@ export const walletModule: RSpaceModule = {
],
acceptsFeeds: ["economic", "governance"],
outputPaths: [
- { path: "wallets", name: "Wallets", icon: "💳", description: "Connected Safe wallets and EOA accounts" },
+ { path: "budget", name: "Budget", icon: "📊", description: "Budget visualization — balance river timeline" },
{ path: "tokens", name: "Token Balances", icon: "🪙", description: "Token balances across chains" },
- { path: "transactions", name: "Transactions", icon: "📜", description: "Transaction history and transfers" },
+ { path: "flows", name: "Flows", icon: "🔀", description: "Sankey flow diagram with transaction scrubber" },
],
};