@net-protocol/profiles 0.1.3 → 0.1.4

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/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DSRRz8Ug.mjs';
2
+ export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DSRRz8Ug.mjs';
3
3
  export { STORAGE_CONTRACT } from '@net-protocol/storage';
4
4
 
5
5
  /**
@@ -141,5 +141,18 @@ declare function getBioStorageArgs(bio: string): ProfileStorageArgs;
141
141
  * @returns Arguments for Storage.put()
142
142
  */
143
143
  declare function getDisplayNameStorageArgs(displayName: string): ProfileStorageArgs;
144
+ /**
145
+ * Prepare transaction arguments for updating token address
146
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
147
+ *
148
+ * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)
149
+ * @returns Arguments for Storage.put()
150
+ */
151
+ declare function getTokenAddressStorageArgs(tokenAddress: string): ProfileStorageArgs;
152
+ /**
153
+ * Validate that a string is a valid EVM token address
154
+ * Returns true if valid (0x-prefixed, 40 hex characters)
155
+ */
156
+ declare function isValidTokenAddress(address: string): boolean;
144
157
 
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 };
158
+ 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, getTokenAddressStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidTokenAddress, isValidUrl, isValidXUsername, parseProfileMetadata };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { P as ProfileStorageArgs, a as ProfileMetadata } from './types-DSRRz8Ug.js';
2
+ export { B as BasicUserProfileMetadata, b as UseProfileOptions, U as UserDisplayName, c as UserProfile } from './types-DSRRz8Ug.js';
3
3
  export { STORAGE_CONTRACT } from '@net-protocol/storage';
4
4
 
5
5
  /**
@@ -141,5 +141,18 @@ declare function getBioStorageArgs(bio: string): ProfileStorageArgs;
141
141
  * @returns Arguments for Storage.put()
142
142
  */
143
143
  declare function getDisplayNameStorageArgs(displayName: string): ProfileStorageArgs;
144
+ /**
145
+ * Prepare transaction arguments for updating token address
146
+ * This is a convenience wrapper around getProfileMetadataStorageArgs
147
+ *
148
+ * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)
149
+ * @returns Arguments for Storage.put()
150
+ */
151
+ declare function getTokenAddressStorageArgs(tokenAddress: string): ProfileStorageArgs;
152
+ /**
153
+ * Validate that a string is a valid EVM token address
154
+ * Returns true if valid (0x-prefixed, 40 hex characters)
155
+ */
156
+ declare function isValidTokenAddress(address: string): boolean;
144
157
 
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 };
158
+ 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, getTokenAddressStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidTokenAddress, isValidUrl, isValidXUsername, parseProfileMetadata };
package/dist/index.js CHANGED
@@ -65,10 +65,12 @@ function parseProfileMetadata(jsonData) {
65
65
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
66
66
  const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
67
67
  const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
68
+ const token_address = parsed?.token_address && typeof parsed.token_address === "string" && parsed.token_address.length > 0 ? parsed.token_address.toLowerCase() : void 0;
68
69
  return {
69
70
  x_username: usernameWithoutAt,
70
71
  bio,
71
- display_name
72
+ display_name,
73
+ token_address
72
74
  };
73
75
  } catch {
74
76
  return void 0;
@@ -106,6 +108,15 @@ function getBioStorageArgs(bio) {
106
108
  function getDisplayNameStorageArgs(displayName) {
107
109
  return getProfileMetadataStorageArgs({ display_name: displayName });
108
110
  }
111
+ function getTokenAddressStorageArgs(tokenAddress) {
112
+ return getProfileMetadataStorageArgs({
113
+ token_address: tokenAddress.toLowerCase()
114
+ });
115
+ }
116
+ function isValidTokenAddress(address) {
117
+ if (!address) return false;
118
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
119
+ }
109
120
 
110
121
  Object.defineProperty(exports, "STORAGE_CONTRACT", {
111
122
  enumerable: true,
@@ -124,10 +135,12 @@ exports.getDisplayNameStorageArgs = getDisplayNameStorageArgs;
124
135
  exports.getProfileCanvasStorageArgs = getProfileCanvasStorageArgs;
125
136
  exports.getProfileMetadataStorageArgs = getProfileMetadataStorageArgs;
126
137
  exports.getProfilePictureStorageArgs = getProfilePictureStorageArgs;
138
+ exports.getTokenAddressStorageArgs = getTokenAddressStorageArgs;
127
139
  exports.getValueArgForStorage = getValueArgForStorage;
128
140
  exports.getXUsernameStorageArgs = getXUsernameStorageArgs;
129
141
  exports.isValidBio = isValidBio;
130
142
  exports.isValidDisplayName = isValidDisplayName;
143
+ exports.isValidTokenAddress = isValidTokenAddress;
131
144
  exports.isValidUrl = isValidUrl;
132
145
  exports.isValidXUsername = isValidXUsername;
133
146
  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 // 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"]}
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 // Extract token address if present (stored as lowercase)\n const token_address =\n parsed?.token_address &&\n typeof parsed.token_address === \"string\" &&\n parsed.token_address.length > 0\n ? parsed.token_address.toLowerCase()\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\n token_address,\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\n/**\n * Prepare transaction arguments for updating token address\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)\n * @returns Arguments for Storage.put()\n */\nexport function getTokenAddressStorageArgs(\n tokenAddress: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({\n token_address: tokenAddress.toLowerCase(),\n });\n}\n\n/**\n * Validate that a string is a valid EVM token address\n * Returns true if valid (0x-prefixed, 40 hex characters)\n */\nexport function isValidTokenAddress(address: string): boolean {\n if (!address) return false;\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n}\n"]}
package/dist/index.mjs CHANGED
@@ -63,10 +63,12 @@ function parseProfileMetadata(jsonData) {
63
63
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
64
64
  const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
65
65
  const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
66
+ const token_address = parsed?.token_address && typeof parsed.token_address === "string" && parsed.token_address.length > 0 ? parsed.token_address.toLowerCase() : void 0;
66
67
  return {
67
68
  x_username: usernameWithoutAt,
68
69
  bio,
69
- display_name
70
+ display_name,
71
+ token_address
70
72
  };
71
73
  } catch {
72
74
  return void 0;
@@ -104,7 +106,16 @@ function getBioStorageArgs(bio) {
104
106
  function getDisplayNameStorageArgs(displayName) {
105
107
  return getProfileMetadataStorageArgs({ display_name: displayName });
106
108
  }
109
+ function getTokenAddressStorageArgs(tokenAddress) {
110
+ return getProfileMetadataStorageArgs({
111
+ token_address: tokenAddress.toLowerCase()
112
+ });
113
+ }
114
+ function isValidTokenAddress(address) {
115
+ if (!address) return false;
116
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
117
+ }
107
118
 
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 };
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, getBioStorageArgs, getBytesArgsForStorage, getDisplayNameStorageArgs, getProfileCanvasStorageArgs, getProfileMetadataStorageArgs, getProfilePictureStorageArgs, getTokenAddressStorageArgs, getValueArgForStorage, getXUsernameStorageArgs, isValidBio, isValidDisplayName, isValidTokenAddress, isValidUrl, isValidXUsername, parseProfileMetadata };
109
120
  //# sourceMappingURL=index.mjs.map
110
121
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
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"]}
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 // Extract token address if present (stored as lowercase)\n const token_address =\n parsed?.token_address &&\n typeof parsed.token_address === \"string\" &&\n parsed.token_address.length > 0\n ? parsed.token_address.toLowerCase()\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\n token_address,\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\n/**\n * Prepare transaction arguments for updating token address\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)\n * @returns Arguments for Storage.put()\n */\nexport function getTokenAddressStorageArgs(\n tokenAddress: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({\n token_address: tokenAddress.toLowerCase(),\n });\n}\n\n/**\n * Validate that a string is a valid EVM token address\n * Returns true if valid (0x-prefixed, 40 hex characters)\n */\nexport function isValidTokenAddress(address: string): boolean {\n if (!address) return false;\n return /^0x[a-fA-F0-9]{40}$/.test(address);\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-DnEzx4eb.mjs';
3
- export { a as ProfileMetadata, c as UserProfile } from './types-DnEzx4eb.mjs';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DSRRz8Ug.mjs';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DSRRz8Ug.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-DnEzx4eb.js';
3
- export { a as ProfileMetadata, c as UserProfile } from './types-DnEzx4eb.js';
2
+ import { b as UseProfileOptions, B as BasicUserProfileMetadata } from './types-DSRRz8Ug.js';
3
+ export { a as ProfileMetadata, c as UserProfile } from './types-DSRRz8Ug.js';
4
4
 
5
5
  /**
6
6
  * Hook to fetch profile picture URL from storage
package/dist/react.js CHANGED
@@ -39,10 +39,12 @@ function parseProfileMetadata(jsonData) {
39
39
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
40
40
  const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
41
41
  const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
42
+ const token_address = parsed?.token_address && typeof parsed.token_address === "string" && parsed.token_address.length > 0 ? parsed.token_address.toLowerCase() : void 0;
42
43
  return {
43
44
  x_username: usernameWithoutAt,
44
45
  bio,
45
- display_name
46
+ display_name,
47
+ token_address
46
48
  };
47
49
  } catch {
48
50
  return void 0;
@@ -140,11 +142,13 @@ function useBasicUserProfileMetadata({
140
142
  const xUsername = profileMetadata?.x_username;
141
143
  const bio = profileMetadata?.bio;
142
144
  const displayName = profileMetadata?.display_name;
145
+ const tokenAddress = profileMetadata?.token_address;
143
146
  return {
144
147
  profilePicture: profilePicture || void 0,
145
148
  xUsername: xUsername || void 0,
146
149
  bio: bio || void 0,
147
150
  displayName: displayName || void 0,
151
+ tokenAddress: tokenAddress || void 0,
148
152
  isLoading
149
153
  };
150
154
  }
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 // 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"]}
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 // Extract token address if present (stored as lowercase)\n const token_address =\n parsed?.token_address &&\n typeof parsed.token_address === \"string\" &&\n parsed.token_address.length > 0\n ? parsed.token_address.toLowerCase()\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\n token_address,\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\n/**\n * Prepare transaction arguments for updating token address\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)\n * @returns Arguments for Storage.put()\n */\nexport function getTokenAddressStorageArgs(\n tokenAddress: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({\n token_address: tokenAddress.toLowerCase(),\n });\n}\n\n/**\n * Validate that a string is a valid EVM token address\n * Returns true if valid (0x-prefixed, 40 hex characters)\n */\nexport function isValidTokenAddress(address: string): boolean {\n if (!address) return false;\n return /^0x[a-fA-F0-9]{40}$/.test(address);\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 const tokenAddress = profileMetadata?.token_address;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n bio: bio || undefined,\n displayName: displayName || undefined,\n tokenAddress: tokenAddress || undefined,\n isLoading,\n };\n}\n"]}
package/dist/react.mjs CHANGED
@@ -37,10 +37,12 @@ function parseProfileMetadata(jsonData) {
37
37
  const usernameWithoutAt = storedUsername?.startsWith("@") ? storedUsername.slice(1) : storedUsername;
38
38
  const bio = parsed?.bio && typeof parsed.bio === "string" && parsed.bio.length > 0 ? parsed.bio : void 0;
39
39
  const display_name = parsed?.display_name && typeof parsed.display_name === "string" && parsed.display_name.length > 0 ? parsed.display_name : void 0;
40
+ const token_address = parsed?.token_address && typeof parsed.token_address === "string" && parsed.token_address.length > 0 ? parsed.token_address.toLowerCase() : void 0;
40
41
  return {
41
42
  x_username: usernameWithoutAt,
42
43
  bio,
43
- display_name
44
+ display_name,
45
+ token_address
44
46
  };
45
47
  } catch {
46
48
  return void 0;
@@ -138,11 +140,13 @@ function useBasicUserProfileMetadata({
138
140
  const xUsername = profileMetadata?.x_username;
139
141
  const bio = profileMetadata?.bio;
140
142
  const displayName = profileMetadata?.display_name;
143
+ const tokenAddress = profileMetadata?.token_address;
141
144
  return {
142
145
  profilePicture: profilePicture || void 0,
143
146
  xUsername: xUsername || void 0,
144
147
  bio: bio || void 0,
145
148
  displayName: displayName || void 0,
149
+ tokenAddress: tokenAddress || void 0,
146
150
  isLoading
147
151
  };
148
152
  }
@@ -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 // 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"]}
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 // Extract token address if present (stored as lowercase)\n const token_address =\n parsed?.token_address &&\n typeof parsed.token_address === \"string\" &&\n parsed.token_address.length > 0\n ? parsed.token_address.toLowerCase()\n : undefined;\n\n return {\n x_username: usernameWithoutAt,\n bio,\n display_name,\n token_address,\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\n/**\n * Prepare transaction arguments for updating token address\n * This is a convenience wrapper around getProfileMetadataStorageArgs\n *\n * @param tokenAddress - The ERC-20 token contract address (stored as lowercase)\n * @returns Arguments for Storage.put()\n */\nexport function getTokenAddressStorageArgs(\n tokenAddress: string\n): ProfileStorageArgs {\n return getProfileMetadataStorageArgs({\n token_address: tokenAddress.toLowerCase(),\n });\n}\n\n/**\n * Validate that a string is a valid EVM token address\n * Returns true if valid (0x-prefixed, 40 hex characters)\n */\nexport function isValidTokenAddress(address: string): boolean {\n if (!address) return false;\n return /^0x[a-fA-F0-9]{40}$/.test(address);\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 const tokenAddress = profileMetadata?.token_address;\n\n return {\n profilePicture: profilePicture || undefined,\n xUsername: xUsername || undefined,\n bio: bio || undefined,\n displayName: displayName || undefined,\n tokenAddress: tokenAddress || undefined,\n isLoading,\n };\n}\n"]}
@@ -5,6 +5,7 @@ interface ProfileMetadata {
5
5
  x_username?: string;
6
6
  bio?: string;
7
7
  display_name?: string;
8
+ token_address?: string;
8
9
  }
9
10
  /**
10
11
  * User display name derived from ENS or address
@@ -23,6 +24,7 @@ interface BasicUserProfileMetadata {
23
24
  xUsername?: string;
24
25
  bio?: string;
25
26
  displayName?: string;
27
+ tokenAddress?: string;
26
28
  forwardedTo?: string;
27
29
  isLoading: boolean;
28
30
  }
@@ -51,6 +53,7 @@ interface UserProfile {
51
53
  xUsername?: string;
52
54
  bio?: string;
53
55
  displayName?: string;
56
+ tokenAddress?: string;
54
57
  canvas?: string;
55
58
  forwardedTo?: string;
56
59
  }
@@ -5,6 +5,7 @@ interface ProfileMetadata {
5
5
  x_username?: string;
6
6
  bio?: string;
7
7
  display_name?: string;
8
+ token_address?: string;
8
9
  }
9
10
  /**
10
11
  * User display name derived from ENS or address
@@ -23,6 +24,7 @@ interface BasicUserProfileMetadata {
23
24
  xUsername?: string;
24
25
  bio?: string;
25
26
  displayName?: string;
27
+ tokenAddress?: string;
26
28
  forwardedTo?: string;
27
29
  isLoading: boolean;
28
30
  }
@@ -51,6 +53,7 @@ interface UserProfile {
51
53
  xUsername?: string;
52
54
  bio?: string;
53
55
  displayName?: string;
56
+ tokenAddress?: string;
54
57
  canvas?: string;
55
58
  forwardedTo?: string;
56
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@net-protocol/profiles",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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.7",
45
+ "@net-protocol/core": "^0.1.8",
46
46
  "@net-protocol/storage": "^0.1.11",
47
47
  "viem": "^2.31.4"
48
48
  },