src/commands/profile.ts 4.3 K raw
1
import { getRecords } from "@ensdomains/ensjs/public";
2
import { getSubgraphRecords } from "@ensdomains/ensjs/subgraph";
3
import type { Address } from "viem";
4
import colors from "yoctocolors";
5
import {
6
	ensClient,
7
	getNameAndAddress,
8
	type ResolveOptions,
9
	spinner,
10
} from "../utils";
11
12
function printProfile(
13
	name: string | null,
14
	address: Address | null,
15
	records: any,
16
) {
17
	console.log(colors.blue(`\nENS Profile`));
18
	console.log(colors.blue(`============\n`));
19
20
	if (name) {
21
		console.log(`${colors.blue("Name:")}        ${name}`);
22
	}
23
	if (address) {
24
		console.log(`${colors.blue("Address:")}     ${address}`);
25
	}
26
27
	if (records.texts && records.texts.length > 0) {
28
		console.log(`\n${colors.blue("Text Records:")}`);
29
30
		for (const text of records.texts) {
31
			if (text.value) {
32
				const displayKey = formatTextKey(text.key);
33
				console.log(
34
					`${colors.blue(`${displayKey}:`)}${" ".repeat(Math.max(1, 12 - displayKey.length))}${text.value}`,
35
				);
36
			}
37
		}
38
	}
39
40
	if (records.coins && records.coins.length > 0) {
41
		console.log(`\n${colors.blue("Coin Records:")}`);
42
43
		const maxCoinNameLength = Math.max(
44
			...records.coins
45
				.filter((coin: any) => coin.value)
46
				.map((coin: any) => coin.name.toUpperCase().length),
47
		);
48
49
		for (const coin of records.coins) {
50
			if (coin.value) {
51
				const coinName = coin.name.toUpperCase();
52
				const padding = " ".repeat(
53
					Math.max(1, maxCoinNameLength + 1 - coinName.length),
54
				);
55
				console.log(
56
					`${colors.blue(`${coinName}:`)}${padding}      ${coin.value}`,
57
				);
58
			}
59
		}
60
	}
61
62
	if (records.contentHash) {
63
		console.log(`\n${colors.blue("Content Hash:")}`);
64
		console.log(
65
			`${colors.blue("Type:")}        ${records.contentHash.protocolType}`,
66
		);
67
		console.log(
68
			`${colors.blue("Hash:")}        ${records.contentHash.decoded}`,
69
		);
70
	}
71
72
	if (records.resolverAddress) {
73
		console.log(`\n${colors.blue("Resolver:")}    ${records.resolverAddress}`);
74
	}
75
76
	console.log("");
77
}
78
79
function formatTextKey(key: string): string {
80
	const keyMappings: { [key: string]: string } = {
81
		// Global Keys (ENSIP-5)
82
		avatar: "Avatar",
83
		description: "Bio",
84
		display: "Display Name",
85
		email: "Email",
86
		keywords: "Keywords",
87
		mail: "Mail",
88
		notice: "Notice",
89
		location: "Location",
90
		phone: "Phone",
91
		url: "Website",
92
93
		// Service Keys (ENSIP-5)
94
		"com.github": "GitHub",
95
		"com.twitter": "Twitter",
96
		"com.linkedin": "LinkedIn",
97
		"com.discord": "Discord",
98
		"com.warpcast": "Warpcast",
99
		"com.peepeth": "Peepeth",
100
		"io.keybase": "Keybase",
101
		"org.telegram": "Telegram",
102
103
		// Legacy Keys
104
		"vnd.github": "GitHub (Legacy)",
105
		"vnd.peepeth": "Peepeth (Legacy)",
106
		"vnd.twitter": "Twitter (Legacy)",
107
108
		// Additional common records
109
		timezone: "Timezone",
110
		pronouns: "Pronouns",
111
		ipcm: "IPCM",
112
	};
113
114
	return keyMappings[key] || key.charAt(0).toUpperCase() + key.slice(1);
115
}
116
117
export async function profile(options: ResolveOptions) {
118
	spinner.start();
119
120
	const { name, address } = await getNameAndAddress(options);
121
122
	if (!name || !address) {
123
		spinner.stop();
124
		console.log("404: Name not found");
125
		return;
126
	}
127
128
	// Note: If resolverAddress is provided, inform user it's for reference
129
	if (options.resolverAddress) {
130
		spinner.stop();
131
		console.log(`Note: Using custom resolver: ${options.resolverAddress}`);
132
		console.log(
133
			"(Custom resolver support for read operations is limited in current ENS.js version)\n",
134
		);
135
		spinner.start();
136
	}
137
138
	try {
139
		const subgraphRecords = await getSubgraphRecords(ensClient, {
140
			name: name as string,
141
		});
142
		const records = await getRecords(ensClient, {
143
			name: name as string,
144
			coins: [
145
				"ETH",
146
				"BTC",
147
				"LTC",
148
				"DOGE",
149
				"SOL",
150
				"OP",
151
				"BASE",
152
				"SCR",
153
				"MATIC",
154
				"LINEA",
155
			],
156
			texts: [
157
				...(subgraphRecords?.texts || []),
158
				// ENSIP-5 Global Keys
159
				"avatar",
160
				"description",
161
				"display",
162
				"email",
163
				"location",
164
				"phone",
165
				"url",
166
				// ENSIP-5 Service Keys
167
				"com.github",
168
				"com.twitter",
169
				"com.linkedin",
170
				"com.discord",
171
				"com.warpcast",
172
				"io.keybase",
173
				"org.telegram",
174
				// Additional common records
175
				"timezone",
176
				"pronouns",
177
			],
178
			contentHash: true,
179
			abi: true,
180
		});
181
		spinner.stop();
182
183
		printProfile(name, address, records);
184
	} catch (error) {
185
		spinner.stop();
186
		const e = error as { message: string };
187
		console.error("Error fetching profile record:", e.message);
188
	}
189
190
	return;
191
}