@net-protocol/profiles 0.1.0

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.
@@ -0,0 +1,110 @@
1
+ import * as _net_protocol_storage from '@net-protocol/storage';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DTmd-Ngx.mjs';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DTmd-Ngx.mjs';
4
+
5
+ /**
6
+ * Hook to fetch profile picture URL from storage
7
+ *
8
+ * @param options - Chain ID and user address to fetch profile for
9
+ * @returns Profile picture URL and loading state
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const { profilePicture, isLoading } = useProfilePicture({
14
+ * chainId: 8453,
15
+ * userAddress: "0x...",
16
+ * });
17
+ *
18
+ * if (profilePicture) {
19
+ * return <img src={profilePicture} alt="Profile" />;
20
+ * }
21
+ * ```
22
+ */
23
+ declare function useProfilePicture({ chainId, userAddress, enabled, }: UseProfileOptions): {
24
+ profilePicture: string | undefined;
25
+ isLoading: boolean;
26
+ error: Error | undefined;
27
+ rawData: _net_protocol_storage.StorageData | undefined;
28
+ };
29
+
30
+ /**
31
+ * Hook to fetch X/Twitter username from profile metadata storage
32
+ *
33
+ * @param options - Chain ID and user address to fetch profile for
34
+ * @returns X username (without @) and loading state
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const { xUsername, isLoading } = useProfileXUsername({
39
+ * chainId: 8453,
40
+ * userAddress: "0x...",
41
+ * });
42
+ *
43
+ * if (xUsername) {
44
+ * return <a href={`https://x.com/${xUsername}`}>@{xUsername}</a>;
45
+ * }
46
+ * ```
47
+ */
48
+ declare function useProfileXUsername({ chainId, userAddress, enabled, }: UseProfileOptions): {
49
+ xUsername: string | undefined;
50
+ isLoading: boolean;
51
+ error: Error | undefined;
52
+ rawData: _net_protocol_storage.StorageData | undefined;
53
+ };
54
+
55
+ /**
56
+ * Hook to fetch profile canvas HTML content from storage
57
+ *
58
+ * Note: For large canvas content (>20KB), you may need to use XML storage.
59
+ * This hook fetches from regular storage. For large content, consider using
60
+ * useXmlStorage from @net-protocol/storage/react directly.
61
+ *
62
+ * @param options - Chain ID and user address to fetch profile for
63
+ * @returns Canvas HTML content and loading state
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * const { canvas, isLoading } = useProfileCanvas({
68
+ * chainId: 8453,
69
+ * userAddress: "0x...",
70
+ * });
71
+ *
72
+ * if (canvas) {
73
+ * return <iframe srcDoc={canvas} />;
74
+ * }
75
+ * ```
76
+ */
77
+ declare function useProfileCanvas({ chainId, userAddress, enabled, }: UseProfileOptions): {
78
+ canvas: string | undefined;
79
+ isLoading: boolean;
80
+ error: Error | undefined;
81
+ rawData: _net_protocol_storage.StorageData | undefined;
82
+ };
83
+
84
+ /**
85
+ * Hook to fetch basic user profile metadata (profile picture and X username) in a single call
86
+ *
87
+ * This hook is optimized for efficiency - it batches multiple storage reads into a single
88
+ * contract call using wagmi's useReadContracts.
89
+ *
90
+ * @param options - Chain ID and user address to fetch profile for
91
+ * @returns Profile picture URL, X username, and loading state
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * const { profilePicture, xUsername, isLoading } = useBasicUserProfileMetadata({
96
+ * chainId: 8453,
97
+ * userAddress: "0x...",
98
+ * });
99
+ *
100
+ * return (
101
+ * <div>
102
+ * {profilePicture && <img src={profilePicture} alt="Profile" />}
103
+ * {xUsername && <span>@{xUsername}</span>}
104
+ * </div>
105
+ * );
106
+ * ```
107
+ */
108
+ declare function useBasicUserProfileMetadata({ chainId, userAddress, enabled, }: UseProfileOptions): BasicUserProfileMetadata;
109
+
110
+ export { BasicUserProfileMetadata, UseProfileOptions, useBasicUserProfileMetadata, useProfileCanvas, useProfilePicture, useProfileXUsername };
@@ -0,0 +1,110 @@
1
+ import * as _net_protocol_storage from '@net-protocol/storage';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DTmd-Ngx.js';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DTmd-Ngx.js';
4
+
5
+ /**
6
+ * Hook to fetch profile picture URL from storage
7
+ *
8
+ * @param options - Chain ID and user address to fetch profile for
9
+ * @returns Profile picture URL and loading state
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const { profilePicture, isLoading } = useProfilePicture({
14
+ * chainId: 8453,
15
+ * userAddress: "0x...",
16
+ * });
17
+ *
18
+ * if (profilePicture) {
19
+ * return <img src={profilePicture} alt="Profile" />;
20
+ * }
21
+ * ```
22
+ */
23
+ declare function useProfilePicture({ chainId, userAddress, enabled, }: UseProfileOptions): {
24
+ profilePicture: string | undefined;
25
+ isLoading: boolean;
26
+ error: Error | undefined;
27
+ rawData: _net_protocol_storage.StorageData | undefined;
28
+ };
29
+
30
+ /**
31
+ * Hook to fetch X/Twitter username from profile metadata storage
32
+ *
33
+ * @param options - Chain ID and user address to fetch profile for
34
+ * @returns X username (without @) and loading state
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const { xUsername, isLoading } = useProfileXUsername({
39
+ * chainId: 8453,
40
+ * userAddress: "0x...",
41
+ * });
42
+ *
43
+ * if (xUsername) {
44
+ * return <a href={`https://x.com/${xUsername}`}>@{xUsername}</a>;
45
+ * }
46
+ * ```
47
+ */
48
+ declare function useProfileXUsername({ chainId, userAddress, enabled, }: UseProfileOptions): {
49
+ xUsername: string | undefined;
50
+ isLoading: boolean;
51
+ error: Error | undefined;
52
+ rawData: _net_protocol_storage.StorageData | undefined;
53
+ };
54
+
55
+ /**
56
+ * Hook to fetch profile canvas HTML content from storage
57
+ *
58
+ * Note: For large canvas content (>20KB), you may need to use XML storage.
59
+ * This hook fetches from regular storage. For large content, consider using
60
+ * useXmlStorage from @net-protocol/storage/react directly.
61
+ *
62
+ * @param options - Chain ID and user address to fetch profile for
63
+ * @returns Canvas HTML content and loading state
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * const { canvas, isLoading } = useProfileCanvas({
68
+ * chainId: 8453,
69
+ * userAddress: "0x...",
70
+ * });
71
+ *
72
+ * if (canvas) {
73
+ * return <iframe srcDoc={canvas} />;
74
+ * }
75
+ * ```
76
+ */
77
+ declare function useProfileCanvas({ chainId, userAddress, enabled, }: UseProfileOptions): {
78
+ canvas: string | undefined;
79
+ isLoading: boolean;
80
+ error: Error | undefined;
81
+ rawData: _net_protocol_storage.StorageData | undefined;
82
+ };
83
+
84
+ /**
85
+ * Hook to fetch basic user profile metadata (profile picture and X username) in a single call
86
+ *
87
+ * This hook is optimized for efficiency - it batches multiple storage reads into a single
88
+ * contract call using wagmi's useReadContracts.
89
+ *
90
+ * @param options - Chain ID and user address to fetch profile for
91
+ * @returns Profile picture URL, X username, and loading state
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * const { profilePicture, xUsername, isLoading } = useBasicUserProfileMetadata({
96
+ * chainId: 8453,
97
+ * userAddress: "0x...",
98
+ * });
99
+ *
100
+ * return (
101
+ * <div>
102
+ * {profilePicture && <img src={profilePicture} alt="Profile" />}
103
+ * {xUsername && <span>@{xUsername}</span>}
104
+ * </div>
105
+ * );
106
+ * ```
107
+ */
108
+ declare function useBasicUserProfileMetadata({ chainId, userAddress, enabled, }: UseProfileOptions): BasicUserProfileMetadata;
109
+
110
+ export { BasicUserProfileMetadata, UseProfileOptions, useBasicUserProfileMetadata, useProfileCanvas, useProfilePicture, useProfileXUsername };
package/dist/react.js ADDED
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ var react = require('@net-protocol/storage/react');
4
+ var storage = require('@net-protocol/storage');
5
+ var viem = require('viem');
6
+ require('@net-protocol/core');
7
+ var wagmi = require('wagmi');
8
+
9
+ // src/hooks/useProfilePicture.ts
10
+
11
+ // src/constants.ts
12
+ var PROFILE_CANVAS_STORAGE_KEY = "net-beta0.0.1-profile-canvas";
13
+ var PROFILE_PICTURE_STORAGE_KEY = "net-beta0.0.1-profile-picture";
14
+ var PROFILE_METADATA_STORAGE_KEY = "net-beta0.0.1-profile-metadata";
15
+ function useProfilePicture({
16
+ chainId,
17
+ userAddress,
18
+ enabled = true
19
+ }) {
20
+ const { data, isLoading, error } = react.useStorage({
21
+ chainId,
22
+ key: storage.getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY),
23
+ operatorAddress: userAddress,
24
+ enabled
25
+ });
26
+ const profilePicture = data?.value ? viem.hexToString(data.value) : void 0;
27
+ return {
28
+ profilePicture: profilePicture || void 0,
29
+ isLoading,
30
+ error,
31
+ // Also expose raw data for advanced use cases
32
+ rawData: data
33
+ };
34
+ }
35
+ function parseProfileMetadata(jsonData) {
36
+ try {
37
+ const parsed = JSON.parse(jsonData);
38
+ const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
39
+ const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
40
+ return {
41
+ x_username: usernameWithoutAt
42
+ };
43
+ } catch {
44
+ return void 0;
45
+ }
46
+ }
47
+
48
+ // src/hooks/useProfileXUsername.ts
49
+ function useProfileXUsername({
50
+ chainId,
51
+ userAddress,
52
+ enabled = true
53
+ }) {
54
+ const { data, isLoading, error } = react.useStorage({
55
+ chainId,
56
+ key: storage.getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY),
57
+ operatorAddress: userAddress,
58
+ enabled
59
+ });
60
+ let xUsername;
61
+ if (data?.value) {
62
+ try {
63
+ const jsonString = viem.hexToString(data.value);
64
+ const metadata = parseProfileMetadata(jsonString);
65
+ xUsername = metadata?.x_username;
66
+ } catch {
67
+ }
68
+ }
69
+ return {
70
+ xUsername,
71
+ isLoading,
72
+ error,
73
+ // Also expose raw data for advanced use cases
74
+ rawData: data
75
+ };
76
+ }
77
+ function useProfileCanvas({
78
+ chainId,
79
+ userAddress,
80
+ enabled = true
81
+ }) {
82
+ const { data, isLoading, error } = react.useStorage({
83
+ chainId,
84
+ key: storage.getStorageKeyBytes(PROFILE_CANVAS_STORAGE_KEY),
85
+ operatorAddress: userAddress,
86
+ enabled,
87
+ // Use router for automatic chunked storage detection
88
+ useRouter: true
89
+ });
90
+ const canvas = data?.value ? viem.hexToString(data.value) : void 0;
91
+ return {
92
+ canvas: canvas || void 0,
93
+ isLoading,
94
+ error,
95
+ // Also expose raw data for advanced use cases
96
+ rawData: data
97
+ };
98
+ }
99
+ function useBasicUserProfileMetadata({
100
+ chainId,
101
+ userAddress,
102
+ enabled = true
103
+ }) {
104
+ const profilePictureKey = storage.getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY);
105
+ const profileMetadataKey = storage.getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY);
106
+ const {
107
+ data: results,
108
+ isLoading,
109
+ error
110
+ } = wagmi.useReadContracts({
111
+ contracts: [
112
+ {
113
+ address: storage.STORAGE_CONTRACT.address,
114
+ abi: storage.STORAGE_CONTRACT.abi,
115
+ functionName: "get",
116
+ args: [profilePictureKey, userAddress],
117
+ chainId
118
+ },
119
+ {
120
+ address: storage.STORAGE_CONTRACT.address,
121
+ abi: storage.STORAGE_CONTRACT.abi,
122
+ functionName: "get",
123
+ args: [profileMetadataKey, userAddress],
124
+ chainId
125
+ }
126
+ ],
127
+ query: {
128
+ enabled: enabled && !!userAddress && !!chainId
129
+ }
130
+ });
131
+ const profilePictureHex = results?.[0]?.result?.[1] || void 0;
132
+ const profileMetadataJsonHex = results?.[1]?.result?.[1] || void 0;
133
+ const profilePicture = profilePictureHex ? viem.hexToString(profilePictureHex) : void 0;
134
+ const profileMetadataJson = profileMetadataJsonHex ? viem.hexToString(profileMetadataJsonHex) : void 0;
135
+ const profileMetadata = profileMetadataJson ? parseProfileMetadata(profileMetadataJson) : void 0;
136
+ const xUsername = profileMetadata?.x_username;
137
+ return {
138
+ profilePicture: profilePicture || void 0,
139
+ xUsername: xUsername || void 0,
140
+ isLoading
141
+ };
142
+ }
143
+
144
+ exports.useBasicUserProfileMetadata = useBasicUserProfileMetadata;
145
+ exports.useProfileCanvas = useProfileCanvas;
146
+ exports.useProfilePicture = useProfilePicture;
147
+ exports.useProfileXUsername = useProfileXUsername;
148
+ //# sourceMappingURL=react.js.map
149
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/hooks/useProfilePicture.ts","../src/utils.ts","../src/hooks/useProfileXUsername.ts","../src/hooks/useProfileCanvas.ts","../src/hooks/useBasicUserProfileMetadata.ts"],"names":["useStorage","getStorageKeyBytes","hexToString","useReadContracts","STORAGE_CONTRACT"],"mappings":"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","file":"react.js","sourcesContent":["/**\n * Profile-related storage keys\n *\n * Using descriptive keys under 32 bytes to avoid hashing complexity and work seamlessly\n * with existing storage infrastructure. The key includes app prefix and versioning for\n * clarity and future-proofing.\n *\n * NOTE: if we change these keys, users will not be able to see their profile data\n */\nexport const PROFILE_CANVAS_STORAGE_KEY = \"net-beta0.0.1-profile-canvas\";\nexport const PROFILE_PICTURE_STORAGE_KEY = \"net-beta0.0.1-profile-picture\";\nexport const PROFILE_X_USERNAME_STORAGE_KEY =\n \"net-beta0.0.1-profile-x-username\";\nexport const PROFILE_METADATA_STORAGE_KEY = \"net-beta0.0.1-profile-metadata\";\n\n/**\n * Topic strings used when writing to storage\n * These are the second argument to Storage.put()\n */\nexport const PROFILE_PICTURE_TOPIC = \"profile-picture\";\nexport const PROFILE_METADATA_TOPIC = \"profile-metadata\";\nexport const PROFILE_CANVAS_TOPIC = \"profile-canvas\";\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_PICTURE_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch profile picture URL from storage\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Profile picture URL and loading state\n *\n * @example\n * ```tsx\n * const { profilePicture, isLoading } = useProfilePicture({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (profilePicture) {\n * return <img src={profilePicture} alt=\"Profile\" />;\n * }\n * ```\n */\nexport function useProfilePicture({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n });\n\n // Convert hex value to string URL\n const profilePicture = data?.value ? hexToString(data.value as any) : undefined;\n\n return {\n profilePicture: profilePicture || undefined,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { stringToHex } from \"viem\";\nimport { toBytes32 } from \"@net-protocol/core\";\nimport {\n PROFILE_PICTURE_STORAGE_KEY,\n PROFILE_METADATA_STORAGE_KEY,\n PROFILE_CANVAS_STORAGE_KEY,\n PROFILE_PICTURE_TOPIC,\n PROFILE_METADATA_TOPIC,\n PROFILE_CANVAS_TOPIC,\n} from \"./constants\";\nimport type { ProfileMetadata, ProfileStorageArgs } from \"./types\";\n\n/**\n * Convert a string value to hex for storage\n */\nexport function getValueArgForStorage(value: string): `0x${string}` {\n return stringToHex(value);\n}\n\n/**\n * Get storage args (key as bytes32, value as hex)\n */\nexport function getBytesArgsForStorage(\n key: string,\n value: string\n): { bytesKey: `0x${string}`; bytesValue: `0x${string}` } {\n const bytesKey = toBytes32(key) as `0x${string}`;\n const bytesValue = getValueArgForStorage(value);\n return { bytesKey, bytesValue };\n}\n\n/**\n * Prepare transaction arguments for updating profile picture\n *\n * @param imageUrl - The URL of the profile picture\n * @returns Arguments for Storage.put() - [bytesKey, topic, bytesValue]\n *\n * @example\n * ```ts\n * const args = getProfilePictureStorageArgs(\"https://example.com/image.jpg\");\n * // Use with wagmi writeContract:\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfilePictureStorageArgs(\n imageUrl: string\n): ProfileStorageArgs {\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_PICTURE_STORAGE_KEY,\n imageUrl\n );\n return {\n bytesKey,\n topic: PROFILE_PICTURE_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Prepare transaction arguments for updating profile metadata (X username, etc.)\n *\n * @param metadata - Profile metadata object to store\n * @returns Arguments for Storage.put() - [bytesKey, topic, bytesValue]\n *\n * @example\n * ```ts\n * const args = getProfileMetadataStorageArgs({ x_username: \"@myusername\" });\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfileMetadataStorageArgs(\n metadata: ProfileMetadata\n): ProfileStorageArgs {\n const jsonString = JSON.stringify(metadata);\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_METADATA_STORAGE_KEY,\n jsonString\n );\n return {\n bytesKey,\n topic: PROFILE_METADATA_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Prepare transaction arguments for updating X username\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Ensure username has @ prefix for storage\n const normalizedUsername = username.startsWith(\"@\")\n ? username\n : `@${username}`;\n return getProfileMetadataStorageArgs({ x_username: normalizedUsername });\n}\n\n/**\n * Prepare transaction arguments for updating profile canvas (HTML content)\n *\n * @param htmlContent - HTML content for the profile canvas\n * @returns Arguments for Storage.put()\n *\n * @example\n * ```ts\n * const args = getProfileCanvasStorageArgs(\"<div>My custom profile</div>\");\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfileCanvasStorageArgs(\n htmlContent: string\n): ProfileStorageArgs {\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_CANVAS_STORAGE_KEY,\n htmlContent\n );\n return {\n bytesKey,\n topic: PROFILE_CANVAS_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Parse profile metadata JSON and extract profile data\n *\n * @param jsonData - JSON string from storage\n * @returns Parsed profile metadata or undefined if invalid\n */\nexport function parseProfileMetadata(\n jsonData: string\n): ProfileMetadata | undefined {\n try {\n const parsed = JSON.parse(jsonData);\n const storedUsername =\n parsed?.x_username &&\n typeof parsed.x_username === \"string\" &&\n parsed.x_username.length > 0\n ? parsed.x_username\n : undefined;\n\n // Strip @ from stored username for display\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n return {\n x_username: usernameWithoutAt,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Validate that a string is a valid URL\n */\nexport function isValidUrl(url: string): boolean {\n if (!url) return false;\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate X/Twitter username format\n * Returns true if valid (alphanumeric and underscores, 1-15 chars)\n */\nexport function isValidXUsername(username: string): boolean {\n if (!username) return false;\n // Remove @ prefix if present\n const cleanUsername = username.startsWith(\"@\") ? username.slice(1) : username;\n // X usernames: 1-15 chars, alphanumeric and underscores only\n return /^[a-zA-Z0-9_]{1,15}$/.test(cleanUsername);\n}\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_METADATA_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport { parseProfileMetadata } from \"../utils\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch X/Twitter username from profile metadata storage\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns X username (without @) and loading state\n *\n * @example\n * ```tsx\n * const { xUsername, isLoading } = useProfileXUsername({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (xUsername) {\n * return <a href={`https://x.com/${xUsername}`}>@{xUsername}</a>;\n * }\n * ```\n */\nexport function useProfileXUsername({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n });\n\n // Parse JSON metadata to extract X username\n let xUsername: string | undefined;\n if (data?.value) {\n try {\n const jsonString = hexToString(data.value as any);\n const metadata = parseProfileMetadata(jsonString);\n xUsername = metadata?.x_username;\n } catch {\n // Invalid JSON or hex data\n }\n }\n\n return {\n xUsername,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_CANVAS_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch profile canvas HTML content from storage\n *\n * Note: For large canvas content (>20KB), you may need to use XML storage.\n * This hook fetches from regular storage. For large content, consider using\n * useXmlStorage from @net-protocol/storage/react directly.\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Canvas HTML content and loading state\n *\n * @example\n * ```tsx\n * const { canvas, isLoading } = useProfileCanvas({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (canvas) {\n * return <iframe srcDoc={canvas} />;\n * }\n * ```\n */\nexport function useProfileCanvas({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_CANVAS_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n // Use router for automatic chunked storage detection\n useRouter: true,\n });\n\n // Convert hex value to string HTML\n const canvas = data?.value ? hexToString(data.value as any) : undefined;\n\n return {\n canvas: canvas || undefined,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { useReadContracts } from \"wagmi\";\nimport { STORAGE_CONTRACT, getStorageKeyBytes } from \"@net-protocol/storage\";\nimport {\n PROFILE_PICTURE_STORAGE_KEY,\n PROFILE_METADATA_STORAGE_KEY,\n} from \"../constants\";\nimport { hexToString } from \"viem\";\nimport { parseProfileMetadata } from \"../utils\";\nimport type { BasicUserProfileMetadata, UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch basic user profile metadata (profile picture and X username) in a single call\n *\n * This hook is optimized for efficiency - it batches multiple storage reads into a single\n * contract call using wagmi's useReadContracts.\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Profile picture URL, X username, and loading state\n *\n * @example\n * ```tsx\n * const { profilePicture, xUsername, isLoading } = useBasicUserProfileMetadata({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * return (\n * <div>\n * {profilePicture && <img src={profilePicture} alt=\"Profile\" />}\n * {xUsername && <span>@{xUsername}</span>}\n * </div>\n * );\n * ```\n */\nexport function useBasicUserProfileMetadata({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions): BasicUserProfileMetadata {\n const profilePictureKey = getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY);\n const profileMetadataKey = getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY);\n\n const {\n data: results,\n isLoading,\n error,\n } = useReadContracts({\n contracts: [\n {\n address: STORAGE_CONTRACT.address as `0x${string}`,\n abi: STORAGE_CONTRACT.abi,\n functionName: \"get\",\n args: [profilePictureKey, userAddress as `0x${string}`],\n chainId,\n },\n {\n address: STORAGE_CONTRACT.address as `0x${string}`,\n abi: STORAGE_CONTRACT.abi,\n functionName: \"get\",\n args: [profileMetadataKey, userAddress as `0x${string}`],\n chainId,\n },\n ],\n query: {\n enabled: enabled && !!userAddress && !!chainId,\n },\n });\n\n // Parse the results - decode hex data\n // Contract returns tuple [string text, bytes data]\n const profilePictureHex = (results as any)?.[0]?.result?.[1] || undefined;\n const profileMetadataJsonHex = (results as any)?.[1]?.result?.[1] || undefined;\n\n const profilePicture = profilePictureHex\n ? hexToString(profilePictureHex as `0x${string}`)\n : undefined;\n const profileMetadataJson = profileMetadataJsonHex\n ? hexToString(profileMetadataJsonHex as `0x${string}`)\n : undefined;\n\n // Parse X username from JSON metadata\n const profileMetadata = profileMetadataJson\n ? parseProfileMetadata(profileMetadataJson)\n : undefined;\n const xUsername = profileMetadata?.x_username;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n isLoading,\n };\n}\n"]}
package/dist/react.mjs ADDED
@@ -0,0 +1,144 @@
1
+ import { useStorage } from '@net-protocol/storage/react';
2
+ import { getStorageKeyBytes, STORAGE_CONTRACT } from '@net-protocol/storage';
3
+ import { hexToString } from 'viem';
4
+ import '@net-protocol/core';
5
+ import { useReadContracts } from 'wagmi';
6
+
7
+ // src/hooks/useProfilePicture.ts
8
+
9
+ // src/constants.ts
10
+ var PROFILE_CANVAS_STORAGE_KEY = "net-beta0.0.1-profile-canvas";
11
+ var PROFILE_PICTURE_STORAGE_KEY = "net-beta0.0.1-profile-picture";
12
+ var PROFILE_METADATA_STORAGE_KEY = "net-beta0.0.1-profile-metadata";
13
+ function useProfilePicture({
14
+ chainId,
15
+ userAddress,
16
+ enabled = true
17
+ }) {
18
+ const { data, isLoading, error } = useStorage({
19
+ chainId,
20
+ key: getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY),
21
+ operatorAddress: userAddress,
22
+ enabled
23
+ });
24
+ const profilePicture = data?.value ? hexToString(data.value) : void 0;
25
+ return {
26
+ profilePicture: profilePicture || void 0,
27
+ isLoading,
28
+ error,
29
+ // Also expose raw data for advanced use cases
30
+ rawData: data
31
+ };
32
+ }
33
+ function parseProfileMetadata(jsonData) {
34
+ try {
35
+ const parsed = JSON.parse(jsonData);
36
+ const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
37
+ const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
38
+ return {
39
+ x_username: usernameWithoutAt
40
+ };
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ }
45
+
46
+ // src/hooks/useProfileXUsername.ts
47
+ function useProfileXUsername({
48
+ chainId,
49
+ userAddress,
50
+ enabled = true
51
+ }) {
52
+ const { data, isLoading, error } = useStorage({
53
+ chainId,
54
+ key: getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY),
55
+ operatorAddress: userAddress,
56
+ enabled
57
+ });
58
+ let xUsername;
59
+ if (data?.value) {
60
+ try {
61
+ const jsonString = hexToString(data.value);
62
+ const metadata = parseProfileMetadata(jsonString);
63
+ xUsername = metadata?.x_username;
64
+ } catch {
65
+ }
66
+ }
67
+ return {
68
+ xUsername,
69
+ isLoading,
70
+ error,
71
+ // Also expose raw data for advanced use cases
72
+ rawData: data
73
+ };
74
+ }
75
+ function useProfileCanvas({
76
+ chainId,
77
+ userAddress,
78
+ enabled = true
79
+ }) {
80
+ const { data, isLoading, error } = useStorage({
81
+ chainId,
82
+ key: getStorageKeyBytes(PROFILE_CANVAS_STORAGE_KEY),
83
+ operatorAddress: userAddress,
84
+ enabled,
85
+ // Use router for automatic chunked storage detection
86
+ useRouter: true
87
+ });
88
+ const canvas = data?.value ? hexToString(data.value) : void 0;
89
+ return {
90
+ canvas: canvas || void 0,
91
+ isLoading,
92
+ error,
93
+ // Also expose raw data for advanced use cases
94
+ rawData: data
95
+ };
96
+ }
97
+ function useBasicUserProfileMetadata({
98
+ chainId,
99
+ userAddress,
100
+ enabled = true
101
+ }) {
102
+ const profilePictureKey = getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY);
103
+ const profileMetadataKey = getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY);
104
+ const {
105
+ data: results,
106
+ isLoading,
107
+ error
108
+ } = useReadContracts({
109
+ contracts: [
110
+ {
111
+ address: STORAGE_CONTRACT.address,
112
+ abi: STORAGE_CONTRACT.abi,
113
+ functionName: "get",
114
+ args: [profilePictureKey, userAddress],
115
+ chainId
116
+ },
117
+ {
118
+ address: STORAGE_CONTRACT.address,
119
+ abi: STORAGE_CONTRACT.abi,
120
+ functionName: "get",
121
+ args: [profileMetadataKey, userAddress],
122
+ chainId
123
+ }
124
+ ],
125
+ query: {
126
+ enabled: enabled && !!userAddress && !!chainId
127
+ }
128
+ });
129
+ const profilePictureHex = results?.[0]?.result?.[1] || void 0;
130
+ const profileMetadataJsonHex = results?.[1]?.result?.[1] || void 0;
131
+ const profilePicture = profilePictureHex ? hexToString(profilePictureHex) : void 0;
132
+ const profileMetadataJson = profileMetadataJsonHex ? hexToString(profileMetadataJsonHex) : void 0;
133
+ const profileMetadata = profileMetadataJson ? parseProfileMetadata(profileMetadataJson) : void 0;
134
+ const xUsername = profileMetadata?.x_username;
135
+ return {
136
+ profilePicture: profilePicture || void 0,
137
+ xUsername: xUsername || void 0,
138
+ isLoading
139
+ };
140
+ }
141
+
142
+ export { useBasicUserProfileMetadata, useProfileCanvas, useProfilePicture, useProfileXUsername };
143
+ //# sourceMappingURL=react.mjs.map
144
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/hooks/useProfilePicture.ts","../src/utils.ts","../src/hooks/useProfileXUsername.ts","../src/hooks/useProfileCanvas.ts","../src/hooks/useBasicUserProfileMetadata.ts"],"names":["useStorage","getStorageKeyBytes","hexToString"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\n * Profile-related storage keys\n *\n * Using descriptive keys under 32 bytes to avoid hashing complexity and work seamlessly\n * with existing storage infrastructure. The key includes app prefix and versioning for\n * clarity and future-proofing.\n *\n * NOTE: if we change these keys, users will not be able to see their profile data\n */\nexport const PROFILE_CANVAS_STORAGE_KEY = \"net-beta0.0.1-profile-canvas\";\nexport const PROFILE_PICTURE_STORAGE_KEY = \"net-beta0.0.1-profile-picture\";\nexport const PROFILE_X_USERNAME_STORAGE_KEY =\n \"net-beta0.0.1-profile-x-username\";\nexport const PROFILE_METADATA_STORAGE_KEY = \"net-beta0.0.1-profile-metadata\";\n\n/**\n * Topic strings used when writing to storage\n * These are the second argument to Storage.put()\n */\nexport const PROFILE_PICTURE_TOPIC = \"profile-picture\";\nexport const PROFILE_METADATA_TOPIC = \"profile-metadata\";\nexport const PROFILE_CANVAS_TOPIC = \"profile-canvas\";\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_PICTURE_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch profile picture URL from storage\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Profile picture URL and loading state\n *\n * @example\n * ```tsx\n * const { profilePicture, isLoading } = useProfilePicture({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (profilePicture) {\n * return <img src={profilePicture} alt=\"Profile\" />;\n * }\n * ```\n */\nexport function useProfilePicture({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n });\n\n // Convert hex value to string URL\n const profilePicture = data?.value ? hexToString(data.value as any) : undefined;\n\n return {\n profilePicture: profilePicture || undefined,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { stringToHex } from \"viem\";\nimport { toBytes32 } from \"@net-protocol/core\";\nimport {\n PROFILE_PICTURE_STORAGE_KEY,\n PROFILE_METADATA_STORAGE_KEY,\n PROFILE_CANVAS_STORAGE_KEY,\n PROFILE_PICTURE_TOPIC,\n PROFILE_METADATA_TOPIC,\n PROFILE_CANVAS_TOPIC,\n} from \"./constants\";\nimport type { ProfileMetadata, ProfileStorageArgs } from \"./types\";\n\n/**\n * Convert a string value to hex for storage\n */\nexport function getValueArgForStorage(value: string): `0x${string}` {\n return stringToHex(value);\n}\n\n/**\n * Get storage args (key as bytes32, value as hex)\n */\nexport function getBytesArgsForStorage(\n key: string,\n value: string\n): { bytesKey: `0x${string}`; bytesValue: `0x${string}` } {\n const bytesKey = toBytes32(key) as `0x${string}`;\n const bytesValue = getValueArgForStorage(value);\n return { bytesKey, bytesValue };\n}\n\n/**\n * Prepare transaction arguments for updating profile picture\n *\n * @param imageUrl - The URL of the profile picture\n * @returns Arguments for Storage.put() - [bytesKey, topic, bytesValue]\n *\n * @example\n * ```ts\n * const args = getProfilePictureStorageArgs(\"https://example.com/image.jpg\");\n * // Use with wagmi writeContract:\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfilePictureStorageArgs(\n imageUrl: string\n): ProfileStorageArgs {\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_PICTURE_STORAGE_KEY,\n imageUrl\n );\n return {\n bytesKey,\n topic: PROFILE_PICTURE_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Prepare transaction arguments for updating profile metadata (X username, etc.)\n *\n * @param metadata - Profile metadata object to store\n * @returns Arguments for Storage.put() - [bytesKey, topic, bytesValue]\n *\n * @example\n * ```ts\n * const args = getProfileMetadataStorageArgs({ x_username: \"@myusername\" });\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfileMetadataStorageArgs(\n metadata: ProfileMetadata\n): ProfileStorageArgs {\n const jsonString = JSON.stringify(metadata);\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_METADATA_STORAGE_KEY,\n jsonString\n );\n return {\n bytesKey,\n topic: PROFILE_METADATA_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Prepare transaction arguments for updating X username\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Ensure username has @ prefix for storage\n const normalizedUsername = username.startsWith(\"@\")\n ? username\n : `@${username}`;\n return getProfileMetadataStorageArgs({ x_username: normalizedUsername });\n}\n\n/**\n * Prepare transaction arguments for updating profile canvas (HTML content)\n *\n * @param htmlContent - HTML content for the profile canvas\n * @returns Arguments for Storage.put()\n *\n * @example\n * ```ts\n * const args = getProfileCanvasStorageArgs(\"<div>My custom profile</div>\");\n * writeContract({\n * abi: STORAGE_CONTRACT.abi,\n * address: STORAGE_CONTRACT.address,\n * functionName: \"put\",\n * args: [args.bytesKey, args.topic, args.bytesValue],\n * });\n * ```\n */\nexport function getProfileCanvasStorageArgs(\n htmlContent: string\n): ProfileStorageArgs {\n const { bytesKey, bytesValue } = getBytesArgsForStorage(\n PROFILE_CANVAS_STORAGE_KEY,\n htmlContent\n );\n return {\n bytesKey,\n topic: PROFILE_CANVAS_TOPIC,\n bytesValue,\n };\n}\n\n/**\n * Parse profile metadata JSON and extract profile data\n *\n * @param jsonData - JSON string from storage\n * @returns Parsed profile metadata or undefined if invalid\n */\nexport function parseProfileMetadata(\n jsonData: string\n): ProfileMetadata | undefined {\n try {\n const parsed = JSON.parse(jsonData);\n const storedUsername =\n parsed?.x_username &&\n typeof parsed.x_username === \"string\" &&\n parsed.x_username.length > 0\n ? parsed.x_username\n : undefined;\n\n // Strip @ from stored username for display\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n return {\n x_username: usernameWithoutAt,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Validate that a string is a valid URL\n */\nexport function isValidUrl(url: string): boolean {\n if (!url) return false;\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate X/Twitter username format\n * Returns true if valid (alphanumeric and underscores, 1-15 chars)\n */\nexport function isValidXUsername(username: string): boolean {\n if (!username) return false;\n // Remove @ prefix if present\n const cleanUsername = username.startsWith(\"@\") ? username.slice(1) : username;\n // X usernames: 1-15 chars, alphanumeric and underscores only\n return /^[a-zA-Z0-9_]{1,15}$/.test(cleanUsername);\n}\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_METADATA_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport { parseProfileMetadata } from \"../utils\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch X/Twitter username from profile metadata storage\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns X username (without @) and loading state\n *\n * @example\n * ```tsx\n * const { xUsername, isLoading } = useProfileXUsername({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (xUsername) {\n * return <a href={`https://x.com/${xUsername}`}>@{xUsername}</a>;\n * }\n * ```\n */\nexport function useProfileXUsername({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n });\n\n // Parse JSON metadata to extract X username\n let xUsername: string | undefined;\n if (data?.value) {\n try {\n const jsonString = hexToString(data.value as any);\n const metadata = parseProfileMetadata(jsonString);\n xUsername = metadata?.x_username;\n } catch {\n // Invalid JSON or hex data\n }\n }\n\n return {\n xUsername,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { useStorage } from \"@net-protocol/storage/react\";\nimport { getStorageKeyBytes } from \"@net-protocol/storage\";\nimport { PROFILE_CANVAS_STORAGE_KEY } from \"../constants\";\nimport { hexToString } from \"viem\";\nimport type { UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch profile canvas HTML content from storage\n *\n * Note: For large canvas content (>20KB), you may need to use XML storage.\n * This hook fetches from regular storage. For large content, consider using\n * useXmlStorage from @net-protocol/storage/react directly.\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Canvas HTML content and loading state\n *\n * @example\n * ```tsx\n * const { canvas, isLoading } = useProfileCanvas({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * if (canvas) {\n * return <iframe srcDoc={canvas} />;\n * }\n * ```\n */\nexport function useProfileCanvas({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions) {\n const { data, isLoading, error } = useStorage({\n chainId,\n key: getStorageKeyBytes(PROFILE_CANVAS_STORAGE_KEY),\n operatorAddress: userAddress,\n enabled,\n // Use router for automatic chunked storage detection\n useRouter: true,\n });\n\n // Convert hex value to string HTML\n const canvas = data?.value ? hexToString(data.value as any) : undefined;\n\n return {\n canvas: canvas || undefined,\n isLoading,\n error,\n // Also expose raw data for advanced use cases\n rawData: data,\n };\n}\n","import { useReadContracts } from \"wagmi\";\nimport { STORAGE_CONTRACT, getStorageKeyBytes } from \"@net-protocol/storage\";\nimport {\n PROFILE_PICTURE_STORAGE_KEY,\n PROFILE_METADATA_STORAGE_KEY,\n} from \"../constants\";\nimport { hexToString } from \"viem\";\nimport { parseProfileMetadata } from \"../utils\";\nimport type { BasicUserProfileMetadata, UseProfileOptions } from \"../types\";\n\n/**\n * Hook to fetch basic user profile metadata (profile picture and X username) in a single call\n *\n * This hook is optimized for efficiency - it batches multiple storage reads into a single\n * contract call using wagmi's useReadContracts.\n *\n * @param options - Chain ID and user address to fetch profile for\n * @returns Profile picture URL, X username, and loading state\n *\n * @example\n * ```tsx\n * const { profilePicture, xUsername, isLoading } = useBasicUserProfileMetadata({\n * chainId: 8453,\n * userAddress: \"0x...\",\n * });\n *\n * return (\n * <div>\n * {profilePicture && <img src={profilePicture} alt=\"Profile\" />}\n * {xUsername && <span>@{xUsername}</span>}\n * </div>\n * );\n * ```\n */\nexport function useBasicUserProfileMetadata({\n chainId,\n userAddress,\n enabled = true,\n}: UseProfileOptions): BasicUserProfileMetadata {\n const profilePictureKey = getStorageKeyBytes(PROFILE_PICTURE_STORAGE_KEY);\n const profileMetadataKey = getStorageKeyBytes(PROFILE_METADATA_STORAGE_KEY);\n\n const {\n data: results,\n isLoading,\n error,\n } = useReadContracts({\n contracts: [\n {\n address: STORAGE_CONTRACT.address as `0x${string}`,\n abi: STORAGE_CONTRACT.abi,\n functionName: \"get\",\n args: [profilePictureKey, userAddress as `0x${string}`],\n chainId,\n },\n {\n address: STORAGE_CONTRACT.address as `0x${string}`,\n abi: STORAGE_CONTRACT.abi,\n functionName: \"get\",\n args: [profileMetadataKey, userAddress as `0x${string}`],\n chainId,\n },\n ],\n query: {\n enabled: enabled && !!userAddress && !!chainId,\n },\n });\n\n // Parse the results - decode hex data\n // Contract returns tuple [string text, bytes data]\n const profilePictureHex = (results as any)?.[0]?.result?.[1] || undefined;\n const profileMetadataJsonHex = (results as any)?.[1]?.result?.[1] || undefined;\n\n const profilePicture = profilePictureHex\n ? hexToString(profilePictureHex as `0x${string}`)\n : undefined;\n const profileMetadataJson = profileMetadataJsonHex\n ? hexToString(profileMetadataJsonHex as `0x${string}`)\n : undefined;\n\n // Parse X username from JSON metadata\n const profileMetadata = profileMetadataJson\n ? parseProfileMetadata(profileMetadataJson)\n : undefined;\n const xUsername = profileMetadata?.x_username;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n isLoading,\n };\n}\n"]}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Profile metadata stored as JSON in the metadata storage key
3
+ */
4
+ interface ProfileMetadata {
5
+ x_username?: string;
6
+ }
7
+ /**
8
+ * User display name derived from ENS or address
9
+ */
10
+ interface UserDisplayName {
11
+ displayName: string;
12
+ ensName?: string;
13
+ isLoading: boolean;
14
+ error?: Error;
15
+ }
16
+ /**
17
+ * Basic profile metadata returned by useBasicUserProfileMetadata
18
+ */
19
+ interface BasicUserProfileMetadata {
20
+ profilePicture?: string;
21
+ xUsername?: string;
22
+ forwardedTo?: string;
23
+ isLoading: boolean;
24
+ }
25
+ /**
26
+ * Options for profile hooks
27
+ */
28
+ interface UseProfileOptions {
29
+ chainId: number;
30
+ userAddress: string;
31
+ enabled?: boolean;
32
+ }
33
+ /**
34
+ * Arguments prepared for Storage.put() transaction
35
+ */
36
+ interface ProfileStorageArgs {
37
+ bytesKey: `0x${string}`;
38
+ topic: string;
39
+ bytesValue: `0x${string}`;
40
+ }
41
+ /**
42
+ * Full profile data combining all profile fields
43
+ */
44
+ interface UserProfile {
45
+ address: string;
46
+ profilePicture?: string;
47
+ xUsername?: string;
48
+ canvas?: string;
49
+ forwardedTo?: string;
50
+ }
51
+
52
+ export type { BasicUserProfileMetadata as B, ProfileStorageArgs as P, UserDisplayName as U, ProfileMetadata as a, UseProfileOptions as b, UserProfile as c };