diff --git a/site/src/components/Icon/identity-special.svg b/site/src/components/Icon/identity-special.svg
new file mode 100644
index 000000000..2de50c042
--- /dev/null
+++ b/site/src/components/Icon/identity-special.svg
@@ -0,0 +1,11 @@
+
diff --git a/site/src/components/User/Username.jsx b/site/src/components/User/Username.jsx
index a5f1b904f..5db4e50d3 100644
--- a/site/src/components/User/Username.jsx
+++ b/site/src/components/User/Username.jsx
@@ -5,6 +5,10 @@ import { Popup } from "semantic-ui-react";
import TextMinor from "../TextMinor";
import { useDisablePopup } from "../../utils/hooks";
import { truncate } from "../../styles/tailwindcss";
+import { KNOWN_ADDR_MATCHERS } from "../../utils/knownAddr";
+import IdentitySpecial from "../Icon/identity-special.svg";
+import Tooltip from "../Tooltip";
+import { p_12_medium } from "../../styles/text";
const TextUsername = styled(TextMinor)`
white-space: nowrap;
@@ -27,8 +31,20 @@ const TextUsername = styled(TextMinor)`
`}
`;
+const SpecialAccountWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 4px;
+`;
+
+const Text = styled.span`
+ ${p_12_medium}
+ color: var(--textPrimaryContrast);
+`;
+
const Username = ({ address, name, ellipsis, popup, popupContent, noLink }) => {
const disabledPopup = useDisablePopup();
+
let displayAddress;
if (typeof address === "string") {
if (ellipsis) {
@@ -50,16 +66,27 @@ const Username = ({ address, name, ellipsis, popup, popupContent, noLink }) => {
}
}
- const displayName = name ? name : displayAddress;
+ const knownAddr = KNOWN_ADDR_MATCHERS.map((matcher) => matcher(address)).find(
+ Boolean,
+ );
+
+ const displayName = name ? name : knownAddr ? knownAddr : displayAddress;
return (
- {displayName}}
- />
+
+ {knownAddr && (
+ Special account}>
+
+
+ )}
+ {displayName}}
+ />
+
);
};
diff --git a/site/src/utils/knownAddr.js b/site/src/utils/knownAddr.js
new file mode 100644
index 000000000..61ed6bf51
--- /dev/null
+++ b/site/src/utils/knownAddr.js
@@ -0,0 +1,60 @@
+import { TypeRegistry } from "@polkadot/types/create";
+import {
+ formatNumber,
+ isCodec,
+ stringToU8a,
+ u8aEmpty,
+ u8aEq,
+ u8aToBn,
+} from "@polkadot/util";
+
+const registry = new TypeRegistry();
+
+function createAllMatcher(prefix, name) {
+ const test = registry.createType(
+ "AccountId",
+ stringToU8a(prefix.padEnd(32, "\0")),
+ );
+
+ return (addr) => (test.eq(addr) ? name : null);
+}
+
+function createNumMatcher(prefix, name, add) {
+ const test = stringToU8a(prefix);
+
+ // 4 bytes for u32 (more should not hurt, LE)
+ const minLength = test.length + 4;
+
+ return (addr) => {
+ try {
+ const u8a = isCodec(addr)
+ ? addr.toU8a()
+ : registry.createType("AccountId", addr).toU8a();
+
+ return u8a.length >= minLength &&
+ u8aEq(test, u8a.subarray(0, test.length)) &&
+ u8aEmpty(u8a.subarray(minLength))
+ ? `${name} ${formatNumber(
+ u8aToBn(u8a.subarray(test.length, minLength)),
+ )}${add ? ` (${add})` : ""}`
+ : null;
+ } catch (e) {
+ return null;
+ }
+ };
+}
+
+export const KNOWN_ADDR_MATCHERS = [
+ createAllMatcher("modlpy/socie", "Society"),
+ createAllMatcher("modlpy/trsry", "Treasury"),
+ createAllMatcher("modlpy/xcmch", "XCM"),
+ createNumMatcher("modlpy/cfund", "Crowdloan"),
+ // Substrate master
+ createNumMatcher("modlpy/npols\x00", "Pool", "Stash"),
+ createNumMatcher("modlpy/npols\x01", "Pool", "Reward"),
+ // Westend
+ createNumMatcher("modlpy/nopls\x00", "Pool", "Stash"),
+ createNumMatcher("modlpy/nopls\x01", "Pool", "Reward"),
+ createNumMatcher("para", "Parachain"),
+ createNumMatcher("sibl", "Sibling"),
+];