@net-protocol/profiles 0.1.1 → 0.1.3

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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ yarn add @net-protocol/profiles
12
12
 
13
13
  ## Features
14
14
 
15
- - **Read profile data**: Profile picture, X username, canvas content
15
+ - **Read profile data**: Profile picture, X username, bio, canvas content
16
16
  - **Write profile data**: Utilities to prepare Storage.put() transactions
17
17
  - **Efficient batch reads**: `useBasicUserProfileMetadata` batches multiple reads
18
18
  - **Built on net-storage**: Uses the Net Storage SDK for underlying storage operations
@@ -102,6 +102,8 @@ function UpdateProfile() {
102
102
  |-------|-------------|-------|
103
103
  | Profile Picture | URL to your profile image | Any valid URL (HTTPS, IPFS, etc.) |
104
104
  | X Username | Your X (Twitter) handle | Stored without @ prefix (e.g., `myusername`) |
105
+ | Bio | Short profile bio | Max 280 characters |
106
+ | Display Name | User-chosen display name | Max 25 characters |
105
107
  | Canvas | Custom HTML profile page | For advanced customization |
106
108
 
107
109
  ## Storage Keys
@@ -109,7 +111,7 @@ function UpdateProfile() {
109
111
  | Key | Description | Data Format |
110
112
  |-----|-------------|-------------|
111
113
  | `PROFILE_PICTURE_STORAGE_KEY` | Profile picture URL | Plain string (URL) |
112
- | `PROFILE_METADATA_STORAGE_KEY` | Profile metadata JSON | `{ x_username: "handle" }` (no @) |
114
+ | `PROFILE_METADATA_STORAGE_KEY` | Profile metadata JSON | `{ x_username: "handle", bio: "...", display_name: "..." }` |
113
115
  | `PROFILE_CANVAS_STORAGE_KEY` | Custom HTML canvas | HTML string |
114
116
 
115
117
  ## API Reference
@@ -125,11 +127,15 @@ function UpdateProfile() {
125
127
 
126
128
  - `getProfilePictureStorageArgs(imageUrl)` - Prepare picture update args
127
129
  - `getXUsernameStorageArgs(username)` - Prepare X username update args
130
+ - `getBioStorageArgs(bio)` - Prepare bio update args
131
+ - `getDisplayNameStorageArgs(displayName)` - Prepare display name update args
128
132
  - `getProfileMetadataStorageArgs(metadata)` - Prepare metadata update args
129
133
  - `getProfileCanvasStorageArgs(html)` - Prepare canvas update args
130
134
  - `parseProfileMetadata(json)` - Parse metadata JSON
131
135
  - `isValidUrl(url)` - Validate URL format
132
136
  - `isValidXUsername(username)` - Validate X username format
137
+ - `isValidBio(bio)` - Validate bio format (max 280 chars, no control chars)
138
+ - `isValidDisplayName(displayName)` - Validate display name format (max 25 chars, no control chars)
133
139
 
134
140
  ## Dependencies
135
141
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DTmd-Ngx.mjs';
2
- export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DTmd-Ngx.mjs';
1
+ import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DnEzx4eb.mjs';
2
+ export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DnEzx4eb.mjs';
3
3
  export { STORAGE_CONTRACT } from '@net-protocol/storage';
4
4
 
5
5
  /**
@@ -115,5 +115,31 @@ declare function isValidUrl(url: string): boolean;
115
115
  * Returns true if valid (alphanumeric and underscores, 1-15 chars)
116
116
  */
117
117
  declare function isValidXUsername(username: string): boolean;
118
+ /**
119
+ * Validate bio format
120
+ * Returns true if valid (max 280 chars, no control characters except newlines)
121
+ */
122
+ declare function isValidBio(bio: string): boolean;
123
+ /**
124
+ * Validate display name format
125
+ * Returns true if valid (1-25 characters, no control characters except spaces)
126
+ */
127
+ declare function isValidDisplayName(displayName: string): boolean;
128
+ /**
129
+ * Prepare transaction arguments for updating bio
130
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
131
+ *
132
+ * @param bio - The bio text
133
+ * @returns Arguments for Storage.put()
134
+ */
135
+ declare function getBioStorageArgs(bio: string): ProfileStorageArgs;
136
+ /**
137
+ * Prepare transaction arguments for updating display name
138
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
139
+ *
140
+ * @param displayName - The display name (max 25 characters)
141
+ * @returns Arguments for Storage.put()
142
+ */
143
+ declare function getDisplayNameStorageArgs(displayName: string): ProfileStorageArgs;
118
144
 
119
- export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, ProfileMetadata, ProfileStorageArgs, getBytesArgsForStorage, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidUrl, isValidXUsername, parseProfileMetadata };
145
+ export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, ProfileMetadata, ProfileStorageArgs, getBioStorageArgs, getBytesArgsForStorage, getDisplayNameStorageArgs, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidUrl, isValidXUsername, parseProfileMetadata };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DTmd-Ngx.js';
2
- export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DTmd-Ngx.js';
1
+ import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DnEzx4eb.js';
2
+ export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DnEzx4eb.js';
3
3
  export { STORAGE_CONTRACT } from '@net-protocol/storage';
4
4
 
5
5
  /**
@@ -115,5 +115,31 @@ declare function isValidUrl(url: string): boolean;
115
115
  * Returns true if valid (alphanumeric and underscores, 1-15 chars)
116
116
  */
117
117
  declare function isValidXUsername(username: string): boolean;
118
+ /**
119
+ * Validate bio format
120
+ * Returns true if valid (max 280 chars, no control characters except newlines)
121
+ */
122
+ declare function isValidBio(bio: string): boolean;
123
+ /**
124
+ * Validate display name format
125
+ * Returns true if valid (1-25 characters, no control characters except spaces)
126
+ */
127
+ declare function isValidDisplayName(displayName: string): boolean;
128
+ /**
129
+ * Prepare transaction arguments for updating bio
130
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
131
+ *
132
+ * @param bio - The bio text
133
+ * @returns Arguments for Storage.put()
134
+ */
135
+ declare function getBioStorageArgs(bio: string): ProfileStorageArgs;
136
+ /**
137
+ * Prepare transaction arguments for updating display name
138
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
139
+ *
140
+ * @param displayName - The display name (max 25 characters)
141
+ * @returns Arguments for Storage.put()
142
+ */
143
+ declare function getDisplayNameStorageArgs(displayName: string): ProfileStorageArgs;
118
144
 
119
- export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, ProfileMetadata, ProfileStorageArgs, getBytesArgsForStorage, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidUrl, isValidXUsername, parseProfileMetadata };
145
+ export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, ProfileMetadata, ProfileStorageArgs, getBioStorageArgs, getBytesArgsForStorage, getDisplayNameStorageArgs, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidUrl, isValidXUsername, parseProfileMetadata };
package/dist/index.js CHANGED
@@ -63,8 +63,12 @@ function parseProfileMetadata(jsonData) {
63
63
  const parsed = JSON.parse(jsonData);
64
64
  const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
65
65
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
66
+ const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
67
+ const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
66
68
  return {
67
- x_username: usernameWithoutAt
69
+ x_username: usernameWithoutAt,
70
+ bio,
71
+ display_name
68
72
  };
69
73
  } catch {
70
74
  return void 0;
@@ -84,6 +88,24 @@ function isValidXUsername(username) {
84
88
  const cleanUsername = username.startsWith("@") ? username.slice(1) : username;
85
89
  return /^[a-zA-Z0-9_]{1,15}$/.test(cleanUsername);
86
90
  }
91
+ function isValidBio(bio) {
92
+ if (!bio) return false;
93
+ if (bio.length > 280) return false;
94
+ const hasControlChars = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(bio);
95
+ return !hasControlChars;
96
+ }
97
+ function isValidDisplayName(displayName) {
98
+ if (!displayName) return false;
99
+ if (displayName.length > 25) return false;
100
+ const hasControlChars = /[\x00-\x1F\x7F]/.test(displayName);
101
+ return !hasControlChars;
102
+ }
103
+ function getBioStorageArgs(bio) {
104
+ return getProfileMetadataStorageArgs({ bio });
105
+ }
106
+ function getDisplayNameStorageArgs(displayName) {
107
+ return getProfileMetadataStorageArgs({ display_name: displayName });
108
+ }
87
109
 
88
110
  Object.defineProperty(exports, "STORAGE_CONTRACT", {
89
111
  enumerable: true,
@@ -96,12 +118,16 @@ exports.PROFILE_METADATA_TOPIC = PROFILE_METADATA_TOPIC;
96
118
  exports.PROFILE_PICTURE_STORAGE_KEY = PROFILE_PICTURE_STORAGE_KEY;
97
119
  exports.PROFILE_PICTURE_TOPIC = PROFILE_PICTURE_TOPIC;
98
120
  exports.PROFILE_X_USERNAME_STORAGE_KEY = PROFILE_X_USERNAME_STORAGE_KEY;
121
+ exports.getBioStorageArgs = getBioStorageArgs;
99
122
  exports.getBytesArgsForStorage = getBytesArgsForStorage;
123
+ exports.getDisplayNameStorageArgs = getDisplayNameStorageArgs;
100
124
  exports.getProfileCanvasStorageArgs = getProfileCanvasStorageArgs;
101
125
  exports.getProfileMetadataStorageArgs = getProfileMetadataStorageArgs;
102
126
  exports.getProfilePictureStorageArgs = getProfilePictureStorageArgs;
103
127
  exports.getValueArgForStorage = getValueArgForStorage;
104
128
  exports.getXUsernameStorageArgs = getXUsernameStorageArgs;
129
+ exports.isValidBio = isValidBio;
130
+ exports.isValidDisplayName = isValidDisplayName;
105
131
  exports.isValidUrl = isValidUrl;
106
132
  exports.isValidXUsername = isValidXUsername;
107
133
  exports.parseProfileMetadata = parseProfileMetadata;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils.ts"],"names":["stringToHex","toBytes32"],"mappings":"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","file":"index.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 { 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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\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"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils.ts"],"names":["stringToHex","toBytes32"],"mappings":"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","file":"index.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 { 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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n // Extract bio if present\n const bio =\n parsed?.bio && typeof parsed.bio === \"string\" && parsed.bio.length > 0\n ? parsed.bio\n : undefined;\n\n // Extract display name if present\n const display_name =\n parsed?.display_name &&\n typeof parsed.display_name === \"string\" &&\n parsed.display_name.length > 0\n ? parsed.display_name\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\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\n/**\n * Validate bio format\n * Returns true if valid (max 280 chars, no control characters except newlines)\n */\nexport function isValidBio(bio: string): boolean {\n if (!bio) return false;\n if (bio.length > 280) return false;\n // Allow printable characters, spaces, and newlines. Disallow other control chars.\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(bio);\n return !hasControlChars;\n}\n\n/**\n * Validate display name format\n * Returns true if valid (1-25 characters, no control characters except spaces)\n */\nexport function isValidDisplayName(displayName: string): boolean {\n if (!displayName) return false;\n if (displayName.length > 25) return false;\n // Disallow control characters\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x1F\\x7F]/.test(displayName);\n return !hasControlChars;\n}\n\n/**\n * Prepare transaction arguments for updating bio\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param bio - The bio text\n * @returns Arguments for Storage.put()\n */\nexport function getBioStorageArgs(bio: string): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ bio });\n}\n\n/**\n * Prepare transaction arguments for updating display name\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param displayName - The display name (max 25 characters)\n * @returns Arguments for Storage.put()\n */\nexport function getDisplayNameStorageArgs(\n displayName: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ display_name: displayName });\n}\n"]}
package/dist/index.mjs CHANGED
@@ -61,8 +61,12 @@ function parseProfileMetadata(jsonData) {
61
61
  const parsed = JSON.parse(jsonData);
62
62
  const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
63
63
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
64
+ const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
65
+ const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
64
66
  return {
65
- x_username: usernameWithoutAt
67
+ x_username: usernameWithoutAt,
68
+ bio,
69
+ display_name
66
70
  };
67
71
  } catch {
68
72
  return void 0;
@@ -82,7 +86,25 @@ function isValidXUsername(username) {
82
86
  const cleanUsername = username.startsWith("@") ? username.slice(1) : username;
83
87
  return /^[a-zA-Z0-9_]{1,15}$/.test(cleanUsername);
84
88
  }
89
+ function isValidBio(bio) {
90
+ if (!bio) return false;
91
+ if (bio.length > 280) return false;
92
+ const hasControlChars = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(bio);
93
+ return !hasControlChars;
94
+ }
95
+ function isValidDisplayName(displayName) {
96
+ if (!displayName) return false;
97
+ if (displayName.length > 25) return false;
98
+ const hasControlChars = /[\x00-\x1F\x7F]/.test(displayName);
99
+ return !hasControlChars;
100
+ }
101
+ function getBioStorageArgs(bio) {
102
+ return getProfileMetadataStorageArgs({ bio });
103
+ }
104
+ function getDisplayNameStorageArgs(displayName) {
105
+ return getProfileMetadataStorageArgs({ display_name: displayName });
106
+ }
85
107
 
86
- export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, getBytesArgsForStorage, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidUrl, isValidXUsername, parseProfileMetadata };
108
+ export { PROFILE_CANVAS_STORAGE_KEY, PROFILE_CANVAS_TOPIC, PROFILE_METADATA_STORAGE_KEY, PROFILE_METADATA_TOPIC, PROFILE_PICTURE_STORAGE_KEY, PROFILE_PICTURE_TOPIC, PROFILE_X_USERNAME_STORAGE_KEY, getBioStorageArgs, getBytesArgsForStorage, getDisplayNameStorageArgs, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidUrl, isValidXUsername, parseProfileMetadata };
87
109
  //# sourceMappingURL=index.mjs.map
88
110
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/utils.ts"],"names":[],"mappings":";;;;;AASO,IAAM,0BAAA,GAA6B;AACnC,IAAM,2BAAA,GAA8B;AACpC,IAAM,8BAAA,GACX;AACK,IAAM,4BAAA,GAA+B;AAMrC,IAAM,qBAAA,GAAwB;AAC9B,IAAM,sBAAA,GAAyB;AAC/B,IAAM,oBAAA,GAAuB;ACN7B,SAAS,sBAAsB,KAAA,EAA8B;AAClE,EAAA,OAAO,YAAY,KAAK,CAAA;AAC1B;AAKO,SAAS,sBAAA,CACd,KACA,KAAA,EACwD;AACxD,EAAA,MAAM,QAAA,GAAW,UAAU,GAAG,CAAA;AAC9B,EAAA,MAAM,UAAA,GAAa,sBAAsB,KAAK,CAAA;AAC9C,EAAA,OAAO,EAAE,UAAU,UAAA,EAAW;AAChC;AAoBO,SAAS,6BACd,QAAA,EACoB;AACpB,EAAA,MAAM,EAAE,QAAA,EAAU,UAAA,EAAW,GAAI,sBAAA;AAAA,IAC/B,2BAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,KAAA,EAAO,qBAAA;AAAA,IACP;AAAA,GACF;AACF;AAmBO,SAAS,8BACd,QAAA,EACoB;AACpB,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA;AAC1C,EAAA,MAAM,EAAE,QAAA,EAAU,UAAA,EAAW,GAAI,sBAAA;AAAA,IAC/B,4BAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,KAAA,EAAO,sBAAA;AAAA,IACP;AAAA,GACF;AACF;AAWO,SAAS,wBAAwB,QAAA,EAAsC;AAE5E,EAAA,MAAM,kBAAA,GAAqB,SAAS,UAAA,CAAW,GAAG,IAC9C,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,GAChB,QAAA;AACJ,EAAA,OAAO,6BAAA,CAA8B,EAAE,UAAA,EAAY,kBAAA,EAAoB,CAAA;AACzE;AAmBO,SAAS,4BACd,WAAA,EACoB;AACpB,EAAA,MAAM,EAAE,QAAA,EAAU,UAAA,EAAW,GAAI,sBAAA;AAAA,IAC/B,0BAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,KAAA,EAAO,oBAAA;AAAA,IACP;AAAA,GACF;AACF;AAQO,SAAS,qBACd,QAAA,EAC6B;AAC7B,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA;AAClC,IAAA,MAAM,cAAA,GACJ,MAAA,EAAQ,UAAA,IACR,OAAO,MAAA,CAAO,UAAA,KAAe,QAAA,IAC7B,MAAA,CAAO,UAAA,CAAW,MAAA,GAAS,CAAA,GACvB,MAAA,CAAO,UAAA,GACP,KAAA,CAAA;AAGN,IAAA,MAAM,iBAAA,GAAoB,gBAAgB,UAAA,CAAW,GAAG,IACpD,cAAA,CAAe,KAAA,CAAM,CAAC,CAAA,GACtB,cAAA;AAEJ,IAAA,OAAO;AAAA,MACL,UAAA,EAAY;AAAA,KACd;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAKO,SAAS,WAAW,GAAA,EAAsB;AAC/C,EAAA,IAAI,CAAC,KAAK,OAAO,KAAA;AACjB,EAAA,IAAI;AACF,IAAA,IAAI,IAAI,GAAG,CAAA;AACX,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAMO,SAAS,iBAAiB,QAAA,EAA2B;AAC1D,EAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AAEtB,EAAA,MAAM,aAAA,GAAgB,SAAS,UAAA,CAAW,GAAG,IAAI,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,GAAI,QAAA;AAErE,EAAA,OAAO,sBAAA,CAAuB,KAAK,aAAa,CAAA;AAClD","file":"index.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 { 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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\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"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils.ts"],"names":[],"mappings":"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","file":"index.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 { 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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n // Extract bio if present\n const bio =\n parsed?.bio && typeof parsed.bio === \"string\" && parsed.bio.length > 0\n ? parsed.bio\n : undefined;\n\n // Extract display name if present\n const display_name =\n parsed?.display_name &&\n typeof parsed.display_name === \"string\" &&\n parsed.display_name.length > 0\n ? parsed.display_name\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\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\n/**\n * Validate bio format\n * Returns true if valid (max 280 chars, no control characters except newlines)\n */\nexport function isValidBio(bio: string): boolean {\n if (!bio) return false;\n if (bio.length > 280) return false;\n // Allow printable characters, spaces, and newlines. Disallow other control chars.\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(bio);\n return !hasControlChars;\n}\n\n/**\n * Validate display name format\n * Returns true if valid (1-25 characters, no control characters except spaces)\n */\nexport function isValidDisplayName(displayName: string): boolean {\n if (!displayName) return false;\n if (displayName.length > 25) return false;\n // Disallow control characters\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x1F\\x7F]/.test(displayName);\n return !hasControlChars;\n}\n\n/**\n * Prepare transaction arguments for updating bio\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param bio - The bio text\n * @returns Arguments for Storage.put()\n */\nexport function getBioStorageArgs(bio: string): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ bio });\n}\n\n/**\n * Prepare transaction arguments for updating display name\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param displayName - The display name (max 25 characters)\n * @returns Arguments for Storage.put()\n */\nexport function getDisplayNameStorageArgs(\n displayName: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ display_name: displayName });\n}\n"]}
package/dist/react.d.mts CHANGED
@@ -1,6 +1,6 @@
1
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';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DnEzx4eb.mjs';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DnEzx4eb.mjs';
4
4
 
5
5
  /**
6
6
  * Hook to fetch profile picture URL from storage
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
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';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DnEzx4eb.js';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DnEzx4eb.js';
4
4
 
5
5
  /**
6
6
  * Hook to fetch profile picture URL from storage
package/dist/react.js CHANGED
@@ -37,8 +37,12 @@ function parseProfileMetadata(jsonData) {
37
37
  const parsed = JSON.parse(jsonData);
38
38
  const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
39
39
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
40
+ const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
41
+ const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
40
42
  return {
41
- x_username: usernameWithoutAt
43
+ x_username: usernameWithoutAt,
44
+ bio,
45
+ display_name
42
46
  };
43
47
  } catch {
44
48
  return void 0;
@@ -134,9 +138,13 @@ function useBasicUserProfileMetadata({
134
138
  const profileMetadataJson = profileMetadataJsonHex ? viem.hexToString(profileMetadataJsonHex) : void 0;
135
139
  const profileMetadata = profileMetadataJson ? parseProfileMetadata(profileMetadataJson) : void 0;
136
140
  const xUsername = profileMetadata?.x_username;
141
+ const bio = profileMetadata?.bio;
142
+ const displayName = profileMetadata?.display_name;
137
143
  return {
138
144
  profilePicture: profilePicture || void 0,
139
145
  xUsername: xUsername || void 0,
146
+ bio: bio || void 0,
147
+ displayName: displayName || void 0,
140
148
  isLoading
141
149
  };
142
150
  }
package/dist/react.js.map CHANGED
@@ -1 +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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\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"]}
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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n // Extract bio if present\n const bio =\n parsed?.bio && typeof parsed.bio === \"string\" && parsed.bio.length > 0\n ? parsed.bio\n : undefined;\n\n // Extract display name if present\n const display_name =\n parsed?.display_name &&\n typeof parsed.display_name === \"string\" &&\n parsed.display_name.length > 0\n ? parsed.display_name\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\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\n/**\n * Validate bio format\n * Returns true if valid (max 280 chars, no control characters except newlines)\n */\nexport function isValidBio(bio: string): boolean {\n if (!bio) return false;\n if (bio.length > 280) return false;\n // Allow printable characters, spaces, and newlines. Disallow other control chars.\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(bio);\n return !hasControlChars;\n}\n\n/**\n * Validate display name format\n * Returns true if valid (1-25 characters, no control characters except spaces)\n */\nexport function isValidDisplayName(displayName: string): boolean {\n if (!displayName) return false;\n if (displayName.length > 25) return false;\n // Disallow control characters\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x1F\\x7F]/.test(displayName);\n return !hasControlChars;\n}\n\n/**\n * Prepare transaction arguments for updating bio\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param bio - The bio text\n * @returns Arguments for Storage.put()\n */\nexport function getBioStorageArgs(bio: string): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ bio });\n}\n\n/**\n * Prepare transaction arguments for updating display name\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param displayName - The display name (max 25 characters)\n * @returns Arguments for Storage.put()\n */\nexport function getDisplayNameStorageArgs(\n displayName: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ display_name: displayName });\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, bio, and display name from JSON metadata\n const profileMetadata = profileMetadataJson\n ? parseProfileMetadata(profileMetadataJson)\n : undefined;\n const xUsername = profileMetadata?.x_username;\n const bio = profileMetadata?.bio;\n const displayName = profileMetadata?.display_name;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n bio: bio || undefined,\n displayName: displayName || undefined,\n isLoading,\n };\n}\n"]}
package/dist/react.mjs CHANGED
@@ -35,8 +35,12 @@ function parseProfileMetadata(jsonData) {
35
35
  const parsed = JSON.parse(jsonData);
36
36
  const storedUsername = parsed?.x_username && typeof parsed.x_username === "string" && parsed.x_username.length > 0 ? parsed.x_username : void 0;
37
37
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
38
+ const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
39
+ const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
38
40
  return {
39
- x_username: usernameWithoutAt
41
+ x_username: usernameWithoutAt,
42
+ bio,
43
+ display_name
40
44
  };
41
45
  } catch {
42
46
  return void 0;
@@ -132,9 +136,13 @@ function useBasicUserProfileMetadata({
132
136
  const profileMetadataJson = profileMetadataJsonHex ? hexToString(profileMetadataJsonHex) : void 0;
133
137
  const profileMetadata = profileMetadataJson ? parseProfileMetadata(profileMetadataJson) : void 0;
134
138
  const xUsername = profileMetadata?.x_username;
139
+ const bio = profileMetadata?.bio;
140
+ const displayName = profileMetadata?.display_name;
135
141
  return {
136
142
  profilePicture: profilePicture || void 0,
137
143
  xUsername: xUsername || void 0,
144
+ bio: bio || void 0,
145
+ displayName: displayName || void 0,
138
146
  isLoading
139
147
  };
140
148
  }
@@ -1 +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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\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"]}
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 * Note: Username is stored WITHOUT the @ prefix. The @ is stripped if provided.\n *\n * @param username - X/Twitter username (with or without @)\n * @returns Arguments for Storage.put()\n */\nexport function getXUsernameStorageArgs(username: string): ProfileStorageArgs {\n // Strip @ prefix if present - store username without @\n const normalizedUsername = username.startsWith(\"@\")\n ? username.slice(1)\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 @ if present for backwards compatibility with older stored data\n const usernameWithoutAt = storedUsername?.startsWith(\"@\")\n ? storedUsername.slice(1)\n : storedUsername;\n\n // Extract bio if present\n const bio =\n parsed?.bio && typeof parsed.bio === \"string\" && parsed.bio.length > 0\n ? parsed.bio\n : undefined;\n\n // Extract display name if present\n const display_name =\n parsed?.display_name &&\n typeof parsed.display_name === \"string\" &&\n parsed.display_name.length > 0\n ? parsed.display_name\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\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\n/**\n * Validate bio format\n * Returns true if valid (max 280 chars, no control characters except newlines)\n */\nexport function isValidBio(bio: string): boolean {\n if (!bio) return false;\n if (bio.length > 280) return false;\n // Allow printable characters, spaces, and newlines. Disallow other control chars.\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(bio);\n return !hasControlChars;\n}\n\n/**\n * Validate display name format\n * Returns true if valid (1-25 characters, no control characters except spaces)\n */\nexport function isValidDisplayName(displayName: string): boolean {\n if (!displayName) return false;\n if (displayName.length > 25) return false;\n // Disallow control characters\n // eslint-disable-next-line no-control-regex\n const hasControlChars = /[\\x00-\\x1F\\x7F]/.test(displayName);\n return !hasControlChars;\n}\n\n/**\n * Prepare transaction arguments for updating bio\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param bio - The bio text\n * @returns Arguments for Storage.put()\n */\nexport function getBioStorageArgs(bio: string): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ bio });\n}\n\n/**\n * Prepare transaction arguments for updating display name\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param displayName - The display name (max 25 characters)\n * @returns Arguments for Storage.put()\n */\nexport function getDisplayNameStorageArgs(\n displayName: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({ display_name: displayName });\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, bio, and display name from JSON metadata\n const profileMetadata = profileMetadataJson\n ? parseProfileMetadata(profileMetadataJson)\n : undefined;\n const xUsername = profileMetadata?.x_username;\n const bio = profileMetadata?.bio;\n const displayName = profileMetadata?.display_name;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n bio: bio || undefined,\n displayName: displayName || undefined,\n isLoading,\n };\n}\n"]}
@@ -3,6 +3,8 @@
3
3
  */
4
4
  interface ProfileMetadata {
5
5
  x_username?: string;
6
+ bio?: string;
7
+ display_name?: string;
6
8
  }
7
9
  /**
8
10
  * User display name derived from ENS or address
@@ -19,6 +21,8 @@ interface UserDisplayName {
19
21
  interface BasicUserProfileMetadata {
20
22
  profilePicture?: string;
21
23
  xUsername?: string;
24
+ bio?: string;
25
+ displayName?: string;
22
26
  forwardedTo?: string;
23
27
  isLoading: boolean;
24
28
  }
@@ -45,6 +49,8 @@ interface UserProfile {
45
49
  address: string;
46
50
  profilePicture?: string;
47
51
  xUsername?: string;
52
+ bio?: string;
53
+ displayName?: string;
48
54
  canvas?: string;
49
55
  forwardedTo?: string;
50
56
  }
@@ -3,6 +3,8 @@
3
3
  */
4
4
  interface ProfileMetadata {
5
5
  x_username?: string;
6
+ bio?: string;
7
+ display_name?: string;
6
8
  }
7
9
  /**
8
10
  * User display name derived from ENS or address
@@ -19,6 +21,8 @@ interface UserDisplayName {
19
21
  interface BasicUserProfileMetadata {
20
22
  profilePicture?: string;
21
23
  xUsername?: string;
24
+ bio?: string;
25
+ displayName?: string;
22
26
  forwardedTo?: string;
23
27
  isLoading: boolean;
24
28
  }
@@ -45,6 +49,8 @@ interface UserProfile {
45
49
  address: string;
46
50
  profilePicture?: string;
47
51
  xUsername?: string;
52
+ bio?: string;
53
+ displayName?: string;
48
54
  canvas?: string;
49
55
  forwardedTo?: string;
50
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@net-protocol/profiles",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Net Profiles SDK for reading and writing user profile data on the Net protocol",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -42,7 +42,7 @@
42
42
  "test:ui": "vitest --ui"
43
43
  },
44
44
  "dependencies": {
45
- "@net-protocol/core": "^0.1.6",
45
+ "@net-protocol/core": "^0.1.7",
46
46
  "@net-protocol/storage": "^0.1.11",
47
47
  "viem": "^2.31.4"
48
48
  },