@hybrd/xmtp 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.cache/tsbuildinfo.json +1 -1
  2. package/.turbo/turbo-build.log +42 -2
  3. package/biome.jsonc +1 -1
  4. package/dist/index.cjs +3385 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +860 -0
  7. package/dist/index.d.ts +860 -0
  8. package/dist/index.js +3323 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +39 -46
  11. package/src/service-client.ts +1 -1
  12. package/tsconfig.json +5 -10
  13. package/tsup.config.ts +14 -0
  14. package/.turbo/turbo-lint$colon$fix.log +0 -6
  15. package/.turbo/turbo-typecheck.log +0 -5
  16. package/dist/scripts/generate-keys.d.ts +0 -1
  17. package/dist/scripts/generate-keys.js +0 -19
  18. package/dist/scripts/refresh-identity.d.ts +0 -1
  19. package/dist/scripts/refresh-identity.js +0 -93
  20. package/dist/scripts/register-wallet.d.ts +0 -1
  21. package/dist/scripts/register-wallet.js +0 -75
  22. package/dist/scripts/revoke-all-installations.d.ts +0 -2
  23. package/dist/scripts/revoke-all-installations.js +0 -68
  24. package/dist/scripts/revoke-installations.d.ts +0 -2
  25. package/dist/scripts/revoke-installations.js +0 -62
  26. package/dist/src/abi/l2_resolver.d.ts +0 -992
  27. package/dist/src/abi/l2_resolver.js +0 -699
  28. package/dist/src/client.d.ts +0 -76
  29. package/dist/src/client.js +0 -709
  30. package/dist/src/constants.d.ts +0 -3
  31. package/dist/src/constants.js +0 -6
  32. package/dist/src/index.d.ts +0 -22
  33. package/dist/src/index.js +0 -46
  34. package/dist/src/lib/message-listener.d.ts +0 -69
  35. package/dist/src/lib/message-listener.js +0 -235
  36. package/dist/src/lib/message-listener.test.d.ts +0 -1
  37. package/dist/src/lib/message-listener.test.js +0 -303
  38. package/dist/src/lib/subjects.d.ts +0 -24
  39. package/dist/src/lib/subjects.js +0 -68
  40. package/dist/src/resolver/address-resolver.d.ts +0 -57
  41. package/dist/src/resolver/address-resolver.js +0 -168
  42. package/dist/src/resolver/basename-resolver.d.ts +0 -134
  43. package/dist/src/resolver/basename-resolver.js +0 -409
  44. package/dist/src/resolver/ens-resolver.d.ts +0 -95
  45. package/dist/src/resolver/ens-resolver.js +0 -249
  46. package/dist/src/resolver/index.d.ts +0 -1
  47. package/dist/src/resolver/index.js +0 -1
  48. package/dist/src/resolver/resolver.d.ts +0 -162
  49. package/dist/src/resolver/resolver.js +0 -238
  50. package/dist/src/resolver/xmtp-resolver.d.ts +0 -95
  51. package/dist/src/resolver/xmtp-resolver.js +0 -297
  52. package/dist/src/service-client.d.ts +0 -77
  53. package/dist/src/service-client.js +0 -198
  54. package/dist/src/types.d.ts +0 -123
  55. package/dist/src/types.js +0 -5
package/dist/index.js ADDED
@@ -0,0 +1,3323 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/client.ts
6
+ import { ReactionCodec } from "@xmtp/content-type-reaction";
7
+ import { ReplyCodec } from "@xmtp/content-type-reply";
8
+ import { TransactionReferenceCodec } from "@xmtp/content-type-transaction-reference";
9
+ import { WalletSendCallsCodec } from "@xmtp/content-type-wallet-send-calls";
10
+ import { Client as Client2 } from "@xmtp/node-sdk";
11
+ import { getRandomValues } from "crypto";
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ import { fromString, toString as uint8arraysToString } from "uint8arrays";
16
+ import { createWalletClient, http, toBytes } from "viem";
17
+ import { privateKeyToAccount } from "viem/accounts";
18
+ import { sepolia } from "viem/chains";
19
+
20
+ // scripts/revoke-installations.ts
21
+ async function revokeOldInstallations(signer, inboxId) {
22
+ console.log("\u{1F527} Attempting to revoke old installations...");
23
+ try {
24
+ if (!inboxId) {
25
+ console.log("\u2139\uFE0F No inboxId provided, cannot revoke installations");
26
+ return false;
27
+ }
28
+ const inboxStates = await Client.inboxStateFromInboxIds(
29
+ [inboxId],
30
+ process.env.XMTP_ENV
31
+ );
32
+ if (!inboxStates[0]) {
33
+ console.log("\u274C No inbox state found for the provided inboxId");
34
+ return false;
35
+ }
36
+ const toRevokeInstallationBytes = inboxStates[0].installations.map(
37
+ (i) => i.bytes
38
+ );
39
+ await Client.revokeInstallations(
40
+ signer,
41
+ inboxId,
42
+ toRevokeInstallationBytes,
43
+ process.env.XMTP_ENV
44
+ );
45
+ const resultingStates = await Client.inboxStateFromInboxIds(
46
+ [inboxId],
47
+ process.env.XMTP_ENV
48
+ );
49
+ console.log(
50
+ `\u{1F4CB} Revoked installations: ${toRevokeInstallationBytes.length} installations`
51
+ );
52
+ console.log(
53
+ `\u{1F4CB} Resulting state: ${resultingStates[0]?.installations.length || 0} installations`
54
+ );
55
+ return true;
56
+ } catch (error) {
57
+ console.error("\u274C Error during installation revocation:", error);
58
+ return false;
59
+ }
60
+ }
61
+ async function main() {
62
+ const { XMTP_WALLET_KEY } = process.env;
63
+ const inboxId = process.argv[2];
64
+ if (!XMTP_WALLET_KEY) {
65
+ console.error("\u274C XMTP_WALLET_KEY is required");
66
+ process.exit(1);
67
+ }
68
+ if (!inboxId) {
69
+ console.error("\u274C InboxID is required as CLI argument");
70
+ console.error("Usage: tsx revoke-installations.ts <inboxId>");
71
+ process.exit(1);
72
+ }
73
+ const signer = createSigner(XMTP_WALLET_KEY);
74
+ const identifier = await signer.getIdentifier();
75
+ const address = identifier.identifier;
76
+ console.log(`\u{1F511} Wallet Address: ${address}`);
77
+ console.log(`\u{1F4CB} Inbox ID: ${inboxId}`);
78
+ const success = await revokeOldInstallations(signer, inboxId);
79
+ if (success) {
80
+ console.log("\u2705 Successfully revoked installations");
81
+ } else {
82
+ console.log("\u274C Failed to revoke installations");
83
+ process.exit(1);
84
+ }
85
+ }
86
+ if (import.meta.url === `file://${process.argv[1]}`) {
87
+ main().catch((error) => {
88
+ console.error("\u{1F4A5} Fatal error:", error);
89
+ process.exit(1);
90
+ });
91
+ }
92
+
93
+ // src/client.ts
94
+ var __filename = fileURLToPath(import.meta.url);
95
+ var __dirname = path.dirname(__filename);
96
+ var createUser = (key) => {
97
+ const account = privateKeyToAccount(key);
98
+ return {
99
+ key,
100
+ account,
101
+ wallet: createWalletClient({
102
+ account,
103
+ chain: sepolia,
104
+ transport: http()
105
+ })
106
+ };
107
+ };
108
+ var createSigner = (key) => {
109
+ if (!key || typeof key !== "string") {
110
+ throw new Error("XMTP wallet key must be a non-empty string");
111
+ }
112
+ const sanitizedKey = key.startsWith("0x") ? key : `0x${key}`;
113
+ const user = createUser(sanitizedKey);
114
+ return {
115
+ type: "EOA",
116
+ getIdentifier: () => ({
117
+ identifierKind: 0,
118
+ // Use numeric value to avoid ambient const enum issue
119
+ identifier: user.account.address.toLowerCase()
120
+ }),
121
+ signMessage: async (message) => {
122
+ const signature = await user.wallet.signMessage({
123
+ message,
124
+ account: user.account
125
+ });
126
+ return toBytes(signature);
127
+ }
128
+ };
129
+ };
130
+ async function clearXMTPDatabase(address, env) {
131
+ console.log("\u{1F9F9} Clearing XMTP database to resolve installation limit...");
132
+ const getStorageDirectory = () => {
133
+ const customStoragePath = process.env.XMTP_STORAGE_PATH;
134
+ if (customStoragePath) {
135
+ return path.isAbsolute(customStoragePath) ? customStoragePath : path.resolve(process.cwd(), customStoragePath);
136
+ }
137
+ const projectRoot = process.env.PROJECT_ROOT || path.resolve(__dirname, "../../..");
138
+ return path.join(projectRoot, ".data/xmtp");
139
+ };
140
+ const dbPattern = `${env}-${address}.db3`;
141
+ const storageDir = getStorageDirectory();
142
+ const possiblePaths = [
143
+ storageDir,
144
+ // Legacy fallback paths for backward compatibility
145
+ path.join(process.cwd(), ".data", "xmtp"),
146
+ path.join(process.cwd(), "..", ".data", "xmtp"),
147
+ path.join(process.cwd(), "..", "..", ".data", "xmtp")
148
+ ];
149
+ for (const dir of possiblePaths) {
150
+ try {
151
+ if (fs.existsSync(dir)) {
152
+ const files = fs.readdirSync(dir);
153
+ const matchingFiles = files.filter(
154
+ (file) => file.includes(dbPattern) || file.includes(address) || file.includes(`xmtp-${env}-${address}`)
155
+ );
156
+ for (const file of matchingFiles) {
157
+ const fullPath = path.join(dir, file);
158
+ try {
159
+ fs.unlinkSync(fullPath);
160
+ console.log(`\u2705 Removed: ${fullPath}`);
161
+ } catch (err) {
162
+ console.log(`\u26A0\uFE0F Could not remove ${fullPath}:`, err);
163
+ }
164
+ }
165
+ }
166
+ } catch (err) {
167
+ }
168
+ }
169
+ }
170
+ async function createXMTPClient(privateKey, opts) {
171
+ const { persist = true, maxRetries = 3, storagePath } = opts ?? {};
172
+ let attempt = 0;
173
+ const signer = createSigner(privateKey);
174
+ if (!signer) {
175
+ throw new Error(
176
+ "No signer provided and XMTP_WALLET_KEY environment variable is not set"
177
+ );
178
+ }
179
+ const { XMTP_ENCRYPTION_KEY, XMTP_ENV } = process.env;
180
+ const identifier = await signer.getIdentifier();
181
+ const address = identifier.identifier;
182
+ while (attempt < maxRetries) {
183
+ try {
184
+ console.log(
185
+ `\u{1F504} Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`
186
+ );
187
+ if (!persist) {
188
+ throw new Error(
189
+ "Stateless mode is not supported. XMTP client must run in persistent mode to properly receive and process messages. Set persist: true or remove the persist option to use the default persistent mode."
190
+ );
191
+ }
192
+ if (!XMTP_ENCRYPTION_KEY) {
193
+ throw new Error("XMTP_ENCRYPTION_KEY must be set for persistent mode");
194
+ }
195
+ const dbEncryptionKey = getEncryptionKeyFromHex(XMTP_ENCRYPTION_KEY);
196
+ const dbPath = await getDbPath(
197
+ `${XMTP_ENV || "dev"}-${address}`,
198
+ storagePath
199
+ );
200
+ console.log(`\u{1F4C1} Using database path: ${dbPath}`);
201
+ const client = await Client2.create(signer, {
202
+ dbEncryptionKey,
203
+ env: XMTP_ENV,
204
+ dbPath,
205
+ codecs: [
206
+ new ReplyCodec(),
207
+ new ReactionCodec(),
208
+ new WalletSendCallsCodec(),
209
+ new TransactionReferenceCodec()
210
+ ]
211
+ });
212
+ console.log("\u{1F4E1} Syncing conversations to ensure latest state...");
213
+ await client.conversations.sync();
214
+ await backupDbToPersistentStorage(
215
+ dbPath,
216
+ `${XMTP_ENV || "dev"}-${address}`
217
+ );
218
+ console.log("\u2705 XMTP XmtpClient created");
219
+ console.log(`\u{1F511} Wallet address: ${address}`);
220
+ console.log(`\u{1F310} Environment: ${XMTP_ENV || "dev"}`);
221
+ console.log(`\u{1F4BE} Storage mode: persistent`);
222
+ return client;
223
+ } catch (error) {
224
+ attempt++;
225
+ if (error instanceof Error && error.message.includes("5/5 installations")) {
226
+ console.log(
227
+ `\u{1F4A5} Installation limit reached (attempt ${attempt}/${maxRetries})`
228
+ );
229
+ if (attempt < maxRetries) {
230
+ const identifier2 = await signer.getIdentifier();
231
+ const address2 = identifier2.identifier;
232
+ const inboxIdMatch = error.message.match(/InboxID ([a-f0-9]+)/);
233
+ const inboxId = inboxIdMatch ? inboxIdMatch[1] : void 0;
234
+ const revocationSuccess = await revokeOldInstallations(
235
+ signer,
236
+ inboxId
237
+ );
238
+ if (revocationSuccess) {
239
+ console.log("\u{1F3AF} Installations revoked, retrying connection...");
240
+ } else {
241
+ console.log(
242
+ "\u26A0\uFE0F Installation revocation failed or not needed, clearing database..."
243
+ );
244
+ await clearXMTPDatabase(address2, process.env.XMTP_ENV || "dev");
245
+ }
246
+ const delay = Math.pow(2, attempt) * 1e3;
247
+ console.log(`\u23F3 Waiting ${delay}ms before retry...`);
248
+ await new Promise((resolve) => setTimeout(resolve, delay));
249
+ } else {
250
+ console.error(
251
+ "\u274C Failed to resolve installation limit after all retries"
252
+ );
253
+ console.error("\u{1F4A1} Possible solutions:");
254
+ console.error(" 1. Use a different wallet (generate new keys)");
255
+ console.error(" 2. Switch XMTP environments (dev <-> production)");
256
+ console.error(" 3. Wait and try again later");
257
+ console.error(" 4. Contact XMTP support for manual intervention");
258
+ throw error;
259
+ }
260
+ } else if (error instanceof Error && error.message.includes("Association error: Missing identity update")) {
261
+ console.log(
262
+ `\u{1F504} Identity association error detected (attempt ${attempt}/${maxRetries})`
263
+ );
264
+ if (attempt < maxRetries) {
265
+ console.log("\u{1F527} Attempting automatic identity refresh...");
266
+ try {
267
+ console.log("\u{1F4DD} Creating persistent client to refresh identity...");
268
+ const tempEncryptionKey = XMTP_ENCRYPTION_KEY ? getEncryptionKeyFromHex(XMTP_ENCRYPTION_KEY) : getEncryptionKeyFromHex(generateEncryptionKeyHex());
269
+ const tempClient = await Client2.create(signer, {
270
+ dbEncryptionKey: tempEncryptionKey,
271
+ env: XMTP_ENV,
272
+ dbPath: await getDbPath(
273
+ `${XMTP_ENV || "dev"}-${address}`,
274
+ storagePath
275
+ ),
276
+ codecs: [
277
+ new ReplyCodec(),
278
+ new ReactionCodec(),
279
+ new WalletSendCallsCodec(),
280
+ new TransactionReferenceCodec()
281
+ ]
282
+ });
283
+ console.log("\u{1F4E1} Syncing identity and conversations...");
284
+ await tempClient.conversations.sync();
285
+ console.log(
286
+ "\u2705 Identity refresh successful, retrying original request..."
287
+ );
288
+ const delay = Math.pow(2, attempt) * 1e3;
289
+ console.log(`\u23F3 Waiting ${delay}ms before retry...`);
290
+ await new Promise((resolve) => setTimeout(resolve, delay));
291
+ } catch (refreshError) {
292
+ console.log(`\u274C Identity refresh failed:`, refreshError);
293
+ }
294
+ } else {
295
+ console.error(
296
+ "\u274C Failed to resolve identity association error after all retries"
297
+ );
298
+ console.error(
299
+ "\u{1F4A1} Try running: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity"
300
+ );
301
+ throw error;
302
+ }
303
+ } else {
304
+ throw error;
305
+ }
306
+ }
307
+ }
308
+ throw new Error("Max retries exceeded");
309
+ }
310
+ var generateEncryptionKeyHex = () => {
311
+ const uint8Array = getRandomValues(new Uint8Array(32));
312
+ return uint8arraysToString(uint8Array, "hex");
313
+ };
314
+ var getEncryptionKeyFromHex = (hex) => {
315
+ return fromString(hex, "hex");
316
+ };
317
+ var getDbPath = async (description = "xmtp", storagePath) => {
318
+ const customStoragePath = process.env.XMTP_STORAGE_PATH;
319
+ let volumePath;
320
+ if (customStoragePath) {
321
+ volumePath = path.isAbsolute(customStoragePath) ? customStoragePath : path.resolve(process.cwd(), customStoragePath);
322
+ } else if (storagePath) {
323
+ volumePath = path.isAbsolute(storagePath) ? storagePath : path.resolve(process.cwd(), storagePath);
324
+ } else {
325
+ const projectRoot = process.env.PROJECT_ROOT || path.resolve(__dirname, "../../..");
326
+ volumePath = path.join(projectRoot, ".data/xmtp");
327
+ }
328
+ const dbPath = `${volumePath}/${description}.db3`;
329
+ if (typeof globalThis !== "undefined" && "XMTP_STORAGE" in globalThis) {
330
+ try {
331
+ console.log(`\u{1F4E6} Using Cloudflare R2 storage for: ${dbPath}`);
332
+ const r2Bucket = globalThis.XMTP_STORAGE;
333
+ const remotePath = `xmtp-databases/${description}.db3`;
334
+ try {
335
+ const existingObject = await r2Bucket.head(remotePath);
336
+ if (existingObject) {
337
+ console.log(`\u{1F4E5} Downloading existing database from R2 storage...`);
338
+ if (!fs.existsSync(volumePath)) {
339
+ fs.mkdirSync(volumePath, { recursive: true });
340
+ }
341
+ const object = await r2Bucket.get(remotePath);
342
+ if (object) {
343
+ const fileData = await object.arrayBuffer();
344
+ fs.writeFileSync(dbPath, new Uint8Array(fileData));
345
+ console.log(`\u2705 Database downloaded from R2 storage`);
346
+ }
347
+ } else {
348
+ console.log(`\u{1F4DD} No existing database found in R2 storage`);
349
+ }
350
+ } catch (error) {
351
+ console.log(`\u26A0\uFE0F Failed to download database from R2 storage:`, error);
352
+ }
353
+ } catch (error) {
354
+ console.log(`\u26A0\uFE0F R2 storage not available:`, error);
355
+ }
356
+ }
357
+ if (!fs.existsSync(volumePath)) {
358
+ fs.mkdirSync(volumePath, { recursive: true });
359
+ }
360
+ return dbPath;
361
+ };
362
+ var backupDbToPersistentStorage = async (dbPath, description) => {
363
+ if (typeof globalThis !== "undefined" && "XMTP_STORAGE" in globalThis && fs.existsSync(dbPath)) {
364
+ try {
365
+ console.log(`\u{1F4E6} Backing up database to R2 storage: ${dbPath}`);
366
+ const r2Bucket = globalThis.XMTP_STORAGE;
367
+ const remotePath = `xmtp-databases/${description}.db3`;
368
+ const fileData = fs.readFileSync(dbPath);
369
+ await r2Bucket.put(remotePath, fileData);
370
+ console.log(`\u2705 Database backed up to R2 storage: ${remotePath}`);
371
+ } catch (error) {
372
+ console.log(`\u26A0\uFE0F Failed to backup database to R2 storage:`, error);
373
+ }
374
+ }
375
+ };
376
+ var logAgentDetails = async (clients) => {
377
+ const clientsByAddress = Array.isArray(clients) ? clients.reduce((acc, XmtpClient) => {
378
+ const address = XmtpClient.accountIdentifier?.identifier ?? "";
379
+ acc[address] = acc[address] ?? [];
380
+ acc[address].push(XmtpClient);
381
+ return acc;
382
+ }, {}) : {
383
+ [clients.accountIdentifier?.identifier ?? ""]: [clients]
384
+ };
385
+ for (const [address, clientGroup] of Object.entries(clientsByAddress)) {
386
+ const firstClient = clientGroup[0];
387
+ const inboxId = firstClient?.inboxId;
388
+ const environments = clientGroup.map((c) => c.options?.env ?? "dev").join(", ");
389
+ console.log(`\x1B[38;2;252;76;52m
390
+ \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
391
+ \u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
392
+ \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
393
+ \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u255D
394
+ \u2588\u2588\u2554\u255D \u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
395
+ \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D
396
+ \x1B[0m`);
397
+ const urls = [`http://xmtp.chat/dm/${address}`];
398
+ const conversations = await firstClient?.conversations.list();
399
+ console.log(`
400
+ \u2713 XMTP XmtpClient:
401
+ \u2022 Address: ${address}
402
+ \u2022 Conversations: ${conversations?.length}
403
+ \u2022 InboxId: ${inboxId}
404
+ \u2022 Networks: ${environments}
405
+ ${urls.map((url) => `\u2022 URL: ${url}`).join("\n")}`);
406
+ }
407
+ };
408
+ function validateEnvironment(vars) {
409
+ const missing = vars.filter((v) => !process.env[v]);
410
+ if (missing.length) {
411
+ try {
412
+ const envPath = path.resolve(process.cwd(), ".env");
413
+ if (fs.existsSync(envPath)) {
414
+ const envVars = fs.readFileSync(envPath, "utf-8").split("\n").filter((line) => line.trim() && !line.startsWith("#")).reduce((acc, line) => {
415
+ const [key, ...val] = line.split("=");
416
+ if (key && val.length) acc[key.trim()] = val.join("=").trim();
417
+ return acc;
418
+ }, {});
419
+ missing.forEach((v) => {
420
+ if (envVars[v]) process.env[v] = envVars[v];
421
+ });
422
+ }
423
+ } catch (e) {
424
+ console.error(e);
425
+ }
426
+ const stillMissing = vars.filter((v) => !process.env[v]);
427
+ if (stillMissing.length) {
428
+ console.error("Missing env vars:", stillMissing.join(", "));
429
+ process.exit(1);
430
+ }
431
+ }
432
+ return vars.reduce((acc, key) => {
433
+ acc[key] = process.env[key];
434
+ return acc;
435
+ }, {});
436
+ }
437
+ async function diagnoseXMTPIdentityIssue(client, inboxId, environment) {
438
+ const suggestions = [];
439
+ const details = {
440
+ environment,
441
+ inboxId,
442
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
443
+ };
444
+ try {
445
+ const inboxState = await client.preferences.inboxStateFromInboxIds([
446
+ inboxId
447
+ ]);
448
+ if (inboxState.length === 0) {
449
+ suggestions.push(
450
+ `Inbox ID ${inboxId} not found in ${environment} environment`
451
+ );
452
+ suggestions.push(
453
+ "Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa"
454
+ );
455
+ suggestions.push(
456
+ "Verify the user has created an identity on this XMTP network"
457
+ );
458
+ details.inboxStateFound = false;
459
+ return { canResolve: false, suggestions, details };
460
+ }
461
+ const inbox = inboxState[0];
462
+ if (!inbox) {
463
+ suggestions.push("Inbox state returned empty data");
464
+ details.inboxStateFound = false;
465
+ return { canResolve: false, suggestions, details };
466
+ }
467
+ details.inboxStateFound = true;
468
+ details.identifierCount = inbox.identifiers?.length || 0;
469
+ if (!inbox.identifiers || inbox.identifiers.length === 0) {
470
+ suggestions.push("Inbox found but has no identifiers");
471
+ suggestions.push("This indicates incomplete identity registration");
472
+ suggestions.push("User may need to re-register their identity on XMTP");
473
+ details.hasIdentifiers = false;
474
+ return { canResolve: false, suggestions, details };
475
+ }
476
+ details.hasIdentifiers = true;
477
+ details.resolvedAddress = inbox.identifiers[0]?.identifier;
478
+ return {
479
+ canResolve: true,
480
+ suggestions: ["Identity resolved successfully"],
481
+ details
482
+ };
483
+ } catch (error) {
484
+ const errorMessage = error instanceof Error ? error.message : String(error);
485
+ details.error = errorMessage;
486
+ if (errorMessage.includes("Association error")) {
487
+ suggestions.push("XMTP identity association error detected");
488
+ suggestions.push(
489
+ "Check if user exists on the correct XMTP environment (dev vs production)"
490
+ );
491
+ suggestions.push(
492
+ "Identity may need to be recreated on the current environment"
493
+ );
494
+ }
495
+ if (errorMessage.includes("Missing identity update")) {
496
+ suggestions.push("Missing identity updates in XMTP network");
497
+ suggestions.push("This can indicate network sync issues");
498
+ suggestions.push("Wait a few minutes and retry, or recreate identity");
499
+ }
500
+ if (errorMessage.includes("database") || errorMessage.includes("storage")) {
501
+ suggestions.push("XMTP local database/storage issue");
502
+ suggestions.push("Try clearing XMTP database and resyncing");
503
+ suggestions.push("Check .data/xmtp directory permissions");
504
+ }
505
+ suggestions.push("Consider testing with a fresh XMTP identity");
506
+ return { canResolve: false, suggestions, details };
507
+ }
508
+ }
509
+ var XMTPConnectionManager = class {
510
+ constructor(privateKey, config = {}) {
511
+ __publicField(this, "client", null);
512
+ __publicField(this, "privateKey");
513
+ __publicField(this, "config");
514
+ __publicField(this, "health");
515
+ __publicField(this, "healthCheckTimer", null);
516
+ __publicField(this, "isReconnecting", false);
517
+ this.privateKey = privateKey;
518
+ this.config = {
519
+ maxRetries: config.maxRetries ?? 5,
520
+ retryDelayMs: config.retryDelayMs ?? 1e3,
521
+ healthCheckIntervalMs: config.healthCheckIntervalMs ?? 3e4,
522
+ connectionTimeoutMs: config.connectionTimeoutMs ?? 1e4,
523
+ reconnectOnFailure: config.reconnectOnFailure ?? true
524
+ };
525
+ this.health = {
526
+ isConnected: false,
527
+ lastHealthCheck: /* @__PURE__ */ new Date(),
528
+ consecutiveFailures: 0,
529
+ totalReconnects: 0,
530
+ avgResponseTime: 0
531
+ };
532
+ }
533
+ async connect(persist = false) {
534
+ if (this.client && this.health.isConnected) {
535
+ return this.client;
536
+ }
537
+ let attempt = 0;
538
+ while (attempt < this.config.maxRetries) {
539
+ try {
540
+ console.log(
541
+ `\u{1F504} XMTP connection attempt ${attempt + 1}/${this.config.maxRetries}`
542
+ );
543
+ this.client = await createXMTPClient(this.privateKey, { persist });
544
+ this.health.isConnected = true;
545
+ this.health.consecutiveFailures = 0;
546
+ this.startHealthMonitoring();
547
+ console.log("\u2705 XMTP client connected successfully");
548
+ return this.client;
549
+ } catch (error) {
550
+ attempt++;
551
+ this.health.consecutiveFailures++;
552
+ console.error(`\u274C XMTP connection attempt ${attempt} failed:`, error);
553
+ if (attempt < this.config.maxRetries) {
554
+ const delay = this.config.retryDelayMs * Math.pow(2, attempt - 1);
555
+ console.log(`\u23F3 Retrying in ${delay}ms...`);
556
+ await this.sleep(delay);
557
+ }
558
+ }
559
+ }
560
+ throw new Error(
561
+ `Failed to connect to XMTP after ${this.config.maxRetries} attempts`
562
+ );
563
+ }
564
+ // private async createClientWithTimeout(persist: boolean): Promise<XmtpClient> {
565
+ // const timeoutPromise = new Promise<never>((_, reject) => {
566
+ // setTimeout(
567
+ // () => reject(new Error("Connection timeout")),
568
+ // this.config.connectionTimeoutMs
569
+ // )
570
+ // })
571
+ // const clientPromise = createXMTPClient(this.signer, { persist })
572
+ // return Promise.race([clientPromise, timeoutPromise])
573
+ // }
574
+ startHealthMonitoring() {
575
+ if (this.healthCheckTimer) {
576
+ clearInterval(this.healthCheckTimer);
577
+ }
578
+ this.healthCheckTimer = setInterval(() => {
579
+ this.performHealthCheck();
580
+ }, this.config.healthCheckIntervalMs);
581
+ }
582
+ async performHealthCheck() {
583
+ if (!this.client) return;
584
+ const startTime = Date.now();
585
+ try {
586
+ await this.client.conversations.list();
587
+ const responseTime = Date.now() - startTime;
588
+ this.health.avgResponseTime = (this.health.avgResponseTime + responseTime) / 2;
589
+ this.health.lastHealthCheck = /* @__PURE__ */ new Date();
590
+ this.health.consecutiveFailures = 0;
591
+ this.health.isConnected = true;
592
+ console.log(`\u{1F493} XMTP health check passed (${responseTime}ms)`);
593
+ } catch (error) {
594
+ this.health.consecutiveFailures++;
595
+ this.health.isConnected = false;
596
+ console.error(`\u{1F494} XMTP health check failed:`, error);
597
+ if (this.config.reconnectOnFailure && !this.isReconnecting) {
598
+ this.handleConnectionFailure();
599
+ }
600
+ }
601
+ }
602
+ async handleConnectionFailure() {
603
+ if (this.isReconnecting) return;
604
+ this.isReconnecting = true;
605
+ this.health.totalReconnects++;
606
+ console.log("\u{1F504} XMTP connection lost, attempting to reconnect...");
607
+ try {
608
+ this.client = null;
609
+ await this.connect();
610
+ console.log("\u2705 XMTP reconnection successful");
611
+ } catch (error) {
612
+ console.error("\u274C XMTP reconnection failed:", error);
613
+ } finally {
614
+ this.isReconnecting = false;
615
+ }
616
+ }
617
+ sleep(ms) {
618
+ return new Promise((resolve) => setTimeout(resolve, ms));
619
+ }
620
+ getHealth() {
621
+ return { ...this.health };
622
+ }
623
+ getClient() {
624
+ return this.client;
625
+ }
626
+ async disconnect() {
627
+ if (this.healthCheckTimer) {
628
+ clearInterval(this.healthCheckTimer);
629
+ this.healthCheckTimer = null;
630
+ }
631
+ this.client = null;
632
+ this.health.isConnected = false;
633
+ console.log("\u{1F50C} XMTP client disconnected");
634
+ }
635
+ };
636
+ async function createXMTPConnectionManager(privateKey, config) {
637
+ const manager = new XMTPConnectionManager(privateKey, config);
638
+ await manager.connect();
639
+ return manager;
640
+ }
641
+ async function resolveUserAddress(client, senderInboxId, maxRetries = 2) {
642
+ let attempt = 0;
643
+ while (attempt < maxRetries) {
644
+ try {
645
+ console.log(
646
+ `\u{1F50D} Resolving user address (attempt ${attempt + 1}/${maxRetries})...`
647
+ );
648
+ const inboxState = await client.preferences.inboxStateFromInboxIds([
649
+ senderInboxId
650
+ ]);
651
+ const firstInbox = inboxState[0];
652
+ if (inboxState.length > 0 && firstInbox?.identifiers && firstInbox.identifiers.length > 0) {
653
+ const userAddress = firstInbox.identifiers[0]?.identifier;
654
+ if (userAddress) {
655
+ console.log("\u2705 Resolved user address:", userAddress);
656
+ return userAddress;
657
+ }
658
+ }
659
+ console.log("\u26A0\uFE0F No identifiers found in inbox state");
660
+ return "unknown";
661
+ } catch (error) {
662
+ attempt++;
663
+ if (error instanceof Error && error.message.includes("Association error: Missing identity update")) {
664
+ console.log(
665
+ `\u{1F504} Identity association error during address resolution (attempt ${attempt}/${maxRetries})`
666
+ );
667
+ if (attempt < maxRetries) {
668
+ console.log(
669
+ "\u{1F527} Attempting automatic identity refresh for address resolution..."
670
+ );
671
+ try {
672
+ console.log("\u{1F4E1} Syncing conversations to refresh identity...");
673
+ await client.conversations.sync();
674
+ console.log("\u23F3 Waiting 2s before retry...");
675
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
676
+ console.log(
677
+ "\u2705 Identity sync completed, retrying address resolution..."
678
+ );
679
+ } catch (refreshError) {
680
+ console.log(`\u274C Identity refresh failed:`, refreshError);
681
+ }
682
+ } else {
683
+ console.error("\u274C Failed to resolve user address after all retries");
684
+ console.error("\u{1F4A1} Identity association issue persists");
685
+ try {
686
+ const diagnosis = await diagnoseXMTPIdentityIssue(
687
+ client,
688
+ senderInboxId,
689
+ process.env.XMTP_ENV || "dev"
690
+ );
691
+ console.log("\u{1F50D} XMTP Identity Diagnosis:");
692
+ diagnosis.suggestions.forEach((suggestion) => {
693
+ console.error(`\u{1F4A1} ${suggestion}`);
694
+ });
695
+ } catch (diagError) {
696
+ console.warn("\u26A0\uFE0F Could not run XMTP identity diagnosis:", diagError);
697
+ }
698
+ return "unknown";
699
+ }
700
+ } else {
701
+ console.error("\u274C Error resolving user address:", error);
702
+ return "unknown";
703
+ }
704
+ }
705
+ }
706
+ return "unknown";
707
+ }
708
+ var startPeriodicBackup = (dbPath, description, intervalMs = 3e5) => {
709
+ return setInterval(async () => {
710
+ try {
711
+ await backupDbToPersistentStorage(dbPath, description);
712
+ } catch (error) {
713
+ console.log(`\u26A0\uFE0F Periodic backup failed:`, error);
714
+ }
715
+ }, intervalMs);
716
+ };
717
+
718
+ // src/constants.ts
719
+ var DEFAULT_OPTIONS = ["yes", "no"];
720
+ var DEFAULT_AMOUNT = "0.1";
721
+ var MAX_USDC_AMOUNT = 10;
722
+
723
+ // src/lib/message-listener.ts
724
+ import { EventEmitter } from "events";
725
+ import { http as http2, createPublicClient } from "viem";
726
+ import { mainnet as mainnet2 } from "viem/chains";
727
+
728
+ // src/resolver/address-resolver.ts
729
+ var AddressResolver = class {
730
+ constructor(client, options = {}) {
731
+ this.client = client;
732
+ __publicField(this, "cache", /* @__PURE__ */ new Map());
733
+ __publicField(this, "maxCacheSize");
734
+ __publicField(this, "cacheTtl");
735
+ this.maxCacheSize = options.maxCacheSize ?? 1e3;
736
+ this.cacheTtl = options.cacheTtl ?? 864e5;
737
+ }
738
+ /**
739
+ * Resolve user address from inbox ID with caching
740
+ */
741
+ async resolveAddress(inboxId, conversationId) {
742
+ const cached = this.getCachedAddress(inboxId);
743
+ if (cached) {
744
+ console.log(`\u2705 Resolved user address from cache: ${cached}`);
745
+ return cached;
746
+ }
747
+ let userAddress = void 0;
748
+ try {
749
+ if (conversationId) {
750
+ const conversation = await this.client.conversations.getConversationById(conversationId);
751
+ if (conversation) {
752
+ userAddress = await this.resolveFromConversation(
753
+ conversation,
754
+ inboxId
755
+ );
756
+ if (userAddress) {
757
+ this.setCachedAddress(inboxId, userAddress);
758
+ console.log(`\u2705 Resolved user address: ${userAddress}`);
759
+ return userAddress;
760
+ }
761
+ }
762
+ }
763
+ userAddress = await this.resolveFromInboxState(inboxId);
764
+ if (userAddress) {
765
+ this.setCachedAddress(inboxId, userAddress);
766
+ console.log(`\u2705 Resolved user address via fallback: ${userAddress}`);
767
+ return userAddress;
768
+ }
769
+ console.log(`\u26A0\uFE0F No identifiers found for inbox ${inboxId}`);
770
+ return null;
771
+ } catch (error) {
772
+ console.error(`\u274C Error resolving user address for ${inboxId}:`, error);
773
+ return null;
774
+ }
775
+ }
776
+ /**
777
+ * Resolve address from conversation members
778
+ */
779
+ async resolveFromConversation(conversation, inboxId) {
780
+ try {
781
+ const members = await conversation.members();
782
+ const sender = members.find(
783
+ (member) => member.inboxId.toLowerCase() === inboxId.toLowerCase()
784
+ );
785
+ if (sender) {
786
+ const ethIdentifier = sender.accountIdentifiers.find(
787
+ (id) => id.identifierKind === 0
788
+ // IdentifierKind.Ethereum
789
+ );
790
+ if (ethIdentifier) {
791
+ return ethIdentifier.identifier;
792
+ } else {
793
+ console.log(`\u26A0\uFE0F No Ethereum identifier found for inbox ${inboxId}`);
794
+ }
795
+ } else {
796
+ console.log(
797
+ `\u26A0\uFE0F Sender not found in conversation members for inbox ${inboxId}`
798
+ );
799
+ }
800
+ } catch (error) {
801
+ console.error(`\u274C Error resolving from conversation members:`, error);
802
+ }
803
+ return null;
804
+ }
805
+ /**
806
+ * Resolve address from inbox state (network fallback)
807
+ */
808
+ async resolveFromInboxState(inboxId) {
809
+ try {
810
+ const inboxState = await this.client.preferences.inboxStateFromInboxIds([
811
+ inboxId
812
+ ]);
813
+ const firstState = inboxState?.[0];
814
+ if (firstState?.identifiers && firstState.identifiers.length > 0) {
815
+ const firstIdentifier = firstState.identifiers[0];
816
+ return firstIdentifier?.identifier;
817
+ }
818
+ } catch (error) {
819
+ console.error(`\u274C Error resolving from inbox state:`, error);
820
+ }
821
+ return null;
822
+ }
823
+ /**
824
+ * Get cached address if not expired
825
+ */
826
+ getCachedAddress(inboxId) {
827
+ const entry = this.cache.get(inboxId);
828
+ if (!entry) return null;
829
+ const now = Date.now();
830
+ if (now - entry.timestamp > this.cacheTtl) {
831
+ this.cache.delete(inboxId);
832
+ return null;
833
+ }
834
+ return entry.address;
835
+ }
836
+ /**
837
+ * Cache address with LRU eviction
838
+ */
839
+ setCachedAddress(inboxId, address) {
840
+ if (this.cache.size >= this.maxCacheSize) {
841
+ const firstKey = this.cache.keys().next().value;
842
+ if (firstKey) {
843
+ this.cache.delete(firstKey);
844
+ }
845
+ }
846
+ this.cache.set(inboxId, {
847
+ address,
848
+ timestamp: Date.now()
849
+ });
850
+ }
851
+ /**
852
+ * Pre-populate cache from existing conversations
853
+ */
854
+ async prePopulateCache() {
855
+ console.log("\u{1F504} Pre-populating address cache...");
856
+ try {
857
+ const conversations = await this.client.conversations.list();
858
+ let cachedCount = 0;
859
+ for (const conversation of conversations) {
860
+ try {
861
+ const members = await conversation.members();
862
+ for (const member of members) {
863
+ const ethIdentifier = member.accountIdentifiers.find(
864
+ (id) => id.identifierKind === 0
865
+ // IdentifierKind.Ethereum
866
+ );
867
+ if (ethIdentifier) {
868
+ this.setCachedAddress(member.inboxId, ethIdentifier.identifier);
869
+ cachedCount++;
870
+ }
871
+ }
872
+ } catch (error) {
873
+ console.error("Error pre-caching conversation members:", error);
874
+ }
875
+ }
876
+ console.log(`\u2705 Pre-cached ${cachedCount} address mappings`);
877
+ } catch (error) {
878
+ console.error("Error pre-populating cache:", error);
879
+ }
880
+ }
881
+ /**
882
+ * Clear the cache
883
+ */
884
+ clearCache() {
885
+ this.cache.clear();
886
+ console.log("\u{1F5D1}\uFE0F Address cache cleared");
887
+ }
888
+ /**
889
+ * Get cache statistics
890
+ */
891
+ getCacheStats() {
892
+ return {
893
+ size: this.cache.size,
894
+ maxSize: this.maxCacheSize
895
+ };
896
+ }
897
+ };
898
+
899
+ // src/resolver/basename-resolver.ts
900
+ import {
901
+ encodePacked,
902
+ keccak256,
903
+ namehash
904
+ } from "viem";
905
+ import { mainnet } from "viem/chains";
906
+
907
+ // src/abi/l2_resolver.ts
908
+ var L2ResolverAbi = [
909
+ {
910
+ inputs: [
911
+ { internalType: "contract ENS", name: "ens_", type: "address" },
912
+ {
913
+ internalType: "address",
914
+ name: "registrarController_",
915
+ type: "address"
916
+ },
917
+ { internalType: "address", name: "reverseRegistrar_", type: "address" },
918
+ { internalType: "address", name: "owner_", type: "address" }
919
+ ],
920
+ stateMutability: "nonpayable",
921
+ type: "constructor"
922
+ },
923
+ { inputs: [], name: "AlreadyInitialized", type: "error" },
924
+ { inputs: [], name: "CantSetSelfAsDelegate", type: "error" },
925
+ { inputs: [], name: "CantSetSelfAsOperator", type: "error" },
926
+ { inputs: [], name: "NewOwnerIsZeroAddress", type: "error" },
927
+ { inputs: [], name: "NoHandoverRequest", type: "error" },
928
+ { inputs: [], name: "Unauthorized", type: "error" },
929
+ {
930
+ anonymous: false,
931
+ inputs: [
932
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
933
+ {
934
+ indexed: true,
935
+ internalType: "uint256",
936
+ name: "contentType",
937
+ type: "uint256"
938
+ }
939
+ ],
940
+ name: "ABIChanged",
941
+ type: "event"
942
+ },
943
+ {
944
+ anonymous: false,
945
+ inputs: [
946
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
947
+ { indexed: false, internalType: "address", name: "a", type: "address" }
948
+ ],
949
+ name: "AddrChanged",
950
+ type: "event"
951
+ },
952
+ {
953
+ anonymous: false,
954
+ inputs: [
955
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
956
+ {
957
+ indexed: false,
958
+ internalType: "uint256",
959
+ name: "coinType",
960
+ type: "uint256"
961
+ },
962
+ {
963
+ indexed: false,
964
+ internalType: "bytes",
965
+ name: "newAddress",
966
+ type: "bytes"
967
+ }
968
+ ],
969
+ name: "AddressChanged",
970
+ type: "event"
971
+ },
972
+ {
973
+ anonymous: false,
974
+ inputs: [
975
+ {
976
+ indexed: true,
977
+ internalType: "address",
978
+ name: "owner",
979
+ type: "address"
980
+ },
981
+ {
982
+ indexed: true,
983
+ internalType: "address",
984
+ name: "operator",
985
+ type: "address"
986
+ },
987
+ { indexed: false, internalType: "bool", name: "approved", type: "bool" }
988
+ ],
989
+ name: "ApprovalForAll",
990
+ type: "event"
991
+ },
992
+ {
993
+ anonymous: false,
994
+ inputs: [
995
+ {
996
+ indexed: false,
997
+ internalType: "address",
998
+ name: "owner",
999
+ type: "address"
1000
+ },
1001
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1002
+ {
1003
+ indexed: true,
1004
+ internalType: "address",
1005
+ name: "delegate",
1006
+ type: "address"
1007
+ },
1008
+ { indexed: true, internalType: "bool", name: "approved", type: "bool" }
1009
+ ],
1010
+ name: "Approved",
1011
+ type: "event"
1012
+ },
1013
+ {
1014
+ anonymous: false,
1015
+ inputs: [
1016
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1017
+ { indexed: false, internalType: "bytes", name: "hash", type: "bytes" }
1018
+ ],
1019
+ name: "ContenthashChanged",
1020
+ type: "event"
1021
+ },
1022
+ {
1023
+ anonymous: false,
1024
+ inputs: [
1025
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1026
+ { indexed: false, internalType: "bytes", name: "name", type: "bytes" },
1027
+ {
1028
+ indexed: false,
1029
+ internalType: "uint16",
1030
+ name: "resource",
1031
+ type: "uint16"
1032
+ },
1033
+ { indexed: false, internalType: "bytes", name: "record", type: "bytes" }
1034
+ ],
1035
+ name: "DNSRecordChanged",
1036
+ type: "event"
1037
+ },
1038
+ {
1039
+ anonymous: false,
1040
+ inputs: [
1041
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1042
+ { indexed: false, internalType: "bytes", name: "name", type: "bytes" },
1043
+ {
1044
+ indexed: false,
1045
+ internalType: "uint16",
1046
+ name: "resource",
1047
+ type: "uint16"
1048
+ }
1049
+ ],
1050
+ name: "DNSRecordDeleted",
1051
+ type: "event"
1052
+ },
1053
+ {
1054
+ anonymous: false,
1055
+ inputs: [
1056
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1057
+ {
1058
+ indexed: false,
1059
+ internalType: "bytes",
1060
+ name: "lastzonehash",
1061
+ type: "bytes"
1062
+ },
1063
+ {
1064
+ indexed: false,
1065
+ internalType: "bytes",
1066
+ name: "zonehash",
1067
+ type: "bytes"
1068
+ }
1069
+ ],
1070
+ name: "DNSZonehashChanged",
1071
+ type: "event"
1072
+ },
1073
+ {
1074
+ anonymous: false,
1075
+ inputs: [
1076
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1077
+ {
1078
+ indexed: true,
1079
+ internalType: "bytes4",
1080
+ name: "interfaceID",
1081
+ type: "bytes4"
1082
+ },
1083
+ {
1084
+ indexed: false,
1085
+ internalType: "address",
1086
+ name: "implementer",
1087
+ type: "address"
1088
+ }
1089
+ ],
1090
+ name: "InterfaceChanged",
1091
+ type: "event"
1092
+ },
1093
+ {
1094
+ anonymous: false,
1095
+ inputs: [
1096
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1097
+ { indexed: false, internalType: "string", name: "name", type: "string" }
1098
+ ],
1099
+ name: "NameChanged",
1100
+ type: "event"
1101
+ },
1102
+ {
1103
+ anonymous: false,
1104
+ inputs: [
1105
+ {
1106
+ indexed: true,
1107
+ internalType: "address",
1108
+ name: "pendingOwner",
1109
+ type: "address"
1110
+ }
1111
+ ],
1112
+ name: "OwnershipHandoverCanceled",
1113
+ type: "event"
1114
+ },
1115
+ {
1116
+ anonymous: false,
1117
+ inputs: [
1118
+ {
1119
+ indexed: true,
1120
+ internalType: "address",
1121
+ name: "pendingOwner",
1122
+ type: "address"
1123
+ }
1124
+ ],
1125
+ name: "OwnershipHandoverRequested",
1126
+ type: "event"
1127
+ },
1128
+ {
1129
+ anonymous: false,
1130
+ inputs: [
1131
+ {
1132
+ indexed: true,
1133
+ internalType: "address",
1134
+ name: "oldOwner",
1135
+ type: "address"
1136
+ },
1137
+ {
1138
+ indexed: true,
1139
+ internalType: "address",
1140
+ name: "newOwner",
1141
+ type: "address"
1142
+ }
1143
+ ],
1144
+ name: "OwnershipTransferred",
1145
+ type: "event"
1146
+ },
1147
+ {
1148
+ anonymous: false,
1149
+ inputs: [
1150
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1151
+ { indexed: false, internalType: "bytes32", name: "x", type: "bytes32" },
1152
+ { indexed: false, internalType: "bytes32", name: "y", type: "bytes32" }
1153
+ ],
1154
+ name: "PubkeyChanged",
1155
+ type: "event"
1156
+ },
1157
+ {
1158
+ anonymous: false,
1159
+ inputs: [
1160
+ {
1161
+ indexed: true,
1162
+ internalType: "address",
1163
+ name: "newRegistrarController",
1164
+ type: "address"
1165
+ }
1166
+ ],
1167
+ name: "RegistrarControllerUpdated",
1168
+ type: "event"
1169
+ },
1170
+ {
1171
+ anonymous: false,
1172
+ inputs: [
1173
+ {
1174
+ indexed: true,
1175
+ internalType: "address",
1176
+ name: "newReverseRegistrar",
1177
+ type: "address"
1178
+ }
1179
+ ],
1180
+ name: "ReverseRegistrarUpdated",
1181
+ type: "event"
1182
+ },
1183
+ {
1184
+ anonymous: false,
1185
+ inputs: [
1186
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1187
+ {
1188
+ indexed: true,
1189
+ internalType: "string",
1190
+ name: "indexedKey",
1191
+ type: "string"
1192
+ },
1193
+ { indexed: false, internalType: "string", name: "key", type: "string" },
1194
+ { indexed: false, internalType: "string", name: "value", type: "string" }
1195
+ ],
1196
+ name: "TextChanged",
1197
+ type: "event"
1198
+ },
1199
+ {
1200
+ anonymous: false,
1201
+ inputs: [
1202
+ { indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
1203
+ {
1204
+ indexed: false,
1205
+ internalType: "uint64",
1206
+ name: "newVersion",
1207
+ type: "uint64"
1208
+ }
1209
+ ],
1210
+ name: "VersionChanged",
1211
+ type: "event"
1212
+ },
1213
+ {
1214
+ inputs: [
1215
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1216
+ { internalType: "uint256", name: "contentTypes", type: "uint256" }
1217
+ ],
1218
+ name: "ABI",
1219
+ outputs: [
1220
+ { internalType: "uint256", name: "", type: "uint256" },
1221
+ { internalType: "bytes", name: "", type: "bytes" }
1222
+ ],
1223
+ stateMutability: "view",
1224
+ type: "function"
1225
+ },
1226
+ {
1227
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1228
+ name: "addr",
1229
+ outputs: [{ internalType: "address payable", name: "", type: "address" }],
1230
+ stateMutability: "view",
1231
+ type: "function"
1232
+ },
1233
+ {
1234
+ inputs: [
1235
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1236
+ { internalType: "uint256", name: "coinType", type: "uint256" }
1237
+ ],
1238
+ name: "addr",
1239
+ outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
1240
+ stateMutability: "view",
1241
+ type: "function"
1242
+ },
1243
+ {
1244
+ inputs: [
1245
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1246
+ { internalType: "address", name: "delegate", type: "address" },
1247
+ { internalType: "bool", name: "approved", type: "bool" }
1248
+ ],
1249
+ name: "approve",
1250
+ outputs: [],
1251
+ stateMutability: "nonpayable",
1252
+ type: "function"
1253
+ },
1254
+ {
1255
+ inputs: [],
1256
+ name: "cancelOwnershipHandover",
1257
+ outputs: [],
1258
+ stateMutability: "payable",
1259
+ type: "function"
1260
+ },
1261
+ {
1262
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1263
+ name: "clearRecords",
1264
+ outputs: [],
1265
+ stateMutability: "nonpayable",
1266
+ type: "function"
1267
+ },
1268
+ {
1269
+ inputs: [
1270
+ { internalType: "address", name: "pendingOwner", type: "address" }
1271
+ ],
1272
+ name: "completeOwnershipHandover",
1273
+ outputs: [],
1274
+ stateMutability: "payable",
1275
+ type: "function"
1276
+ },
1277
+ {
1278
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1279
+ name: "contenthash",
1280
+ outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
1281
+ stateMutability: "view",
1282
+ type: "function"
1283
+ },
1284
+ {
1285
+ inputs: [
1286
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1287
+ { internalType: "bytes32", name: "name", type: "bytes32" },
1288
+ { internalType: "uint16", name: "resource", type: "uint16" }
1289
+ ],
1290
+ name: "dnsRecord",
1291
+ outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
1292
+ stateMutability: "view",
1293
+ type: "function"
1294
+ },
1295
+ {
1296
+ inputs: [],
1297
+ name: "ens",
1298
+ outputs: [{ internalType: "contract ENS", name: "", type: "address" }],
1299
+ stateMutability: "view",
1300
+ type: "function"
1301
+ },
1302
+ {
1303
+ inputs: [
1304
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1305
+ { internalType: "bytes32", name: "name", type: "bytes32" }
1306
+ ],
1307
+ name: "hasDNSRecords",
1308
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
1309
+ stateMutability: "view",
1310
+ type: "function"
1311
+ },
1312
+ {
1313
+ inputs: [
1314
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1315
+ { internalType: "bytes4", name: "interfaceID", type: "bytes4" }
1316
+ ],
1317
+ name: "interfaceImplementer",
1318
+ outputs: [{ internalType: "address", name: "", type: "address" }],
1319
+ stateMutability: "view",
1320
+ type: "function"
1321
+ },
1322
+ {
1323
+ inputs: [
1324
+ { internalType: "address", name: "owner", type: "address" },
1325
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1326
+ { internalType: "address", name: "delegate", type: "address" }
1327
+ ],
1328
+ name: "isApprovedFor",
1329
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
1330
+ stateMutability: "view",
1331
+ type: "function"
1332
+ },
1333
+ {
1334
+ inputs: [
1335
+ { internalType: "address", name: "account", type: "address" },
1336
+ { internalType: "address", name: "operator", type: "address" }
1337
+ ],
1338
+ name: "isApprovedForAll",
1339
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
1340
+ stateMutability: "view",
1341
+ type: "function"
1342
+ },
1343
+ {
1344
+ inputs: [{ internalType: "bytes[]", name: "data", type: "bytes[]" }],
1345
+ name: "multicall",
1346
+ outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }],
1347
+ stateMutability: "nonpayable",
1348
+ type: "function"
1349
+ },
1350
+ {
1351
+ inputs: [
1352
+ { internalType: "bytes32", name: "nodehash", type: "bytes32" },
1353
+ { internalType: "bytes[]", name: "data", type: "bytes[]" }
1354
+ ],
1355
+ name: "multicallWithNodeCheck",
1356
+ outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }],
1357
+ stateMutability: "nonpayable",
1358
+ type: "function"
1359
+ },
1360
+ {
1361
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1362
+ name: "name",
1363
+ outputs: [{ internalType: "string", name: "", type: "string" }],
1364
+ stateMutability: "view",
1365
+ type: "function"
1366
+ },
1367
+ {
1368
+ inputs: [],
1369
+ name: "owner",
1370
+ outputs: [{ internalType: "address", name: "result", type: "address" }],
1371
+ stateMutability: "view",
1372
+ type: "function"
1373
+ },
1374
+ {
1375
+ inputs: [
1376
+ { internalType: "address", name: "pendingOwner", type: "address" }
1377
+ ],
1378
+ name: "ownershipHandoverExpiresAt",
1379
+ outputs: [{ internalType: "uint256", name: "result", type: "uint256" }],
1380
+ stateMutability: "view",
1381
+ type: "function"
1382
+ },
1383
+ {
1384
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1385
+ name: "pubkey",
1386
+ outputs: [
1387
+ { internalType: "bytes32", name: "x", type: "bytes32" },
1388
+ { internalType: "bytes32", name: "y", type: "bytes32" }
1389
+ ],
1390
+ stateMutability: "view",
1391
+ type: "function"
1392
+ },
1393
+ {
1394
+ inputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
1395
+ name: "recordVersions",
1396
+ outputs: [{ internalType: "uint64", name: "", type: "uint64" }],
1397
+ stateMutability: "view",
1398
+ type: "function"
1399
+ },
1400
+ {
1401
+ inputs: [],
1402
+ name: "registrarController",
1403
+ outputs: [{ internalType: "address", name: "", type: "address" }],
1404
+ stateMutability: "view",
1405
+ type: "function"
1406
+ },
1407
+ {
1408
+ inputs: [],
1409
+ name: "renounceOwnership",
1410
+ outputs: [],
1411
+ stateMutability: "payable",
1412
+ type: "function"
1413
+ },
1414
+ {
1415
+ inputs: [],
1416
+ name: "requestOwnershipHandover",
1417
+ outputs: [],
1418
+ stateMutability: "payable",
1419
+ type: "function"
1420
+ },
1421
+ {
1422
+ inputs: [
1423
+ { internalType: "bytes", name: "", type: "bytes" },
1424
+ { internalType: "bytes", name: "data", type: "bytes" }
1425
+ ],
1426
+ name: "resolve",
1427
+ outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
1428
+ stateMutability: "view",
1429
+ type: "function"
1430
+ },
1431
+ {
1432
+ inputs: [],
1433
+ name: "reverseRegistrar",
1434
+ outputs: [{ internalType: "address", name: "", type: "address" }],
1435
+ stateMutability: "view",
1436
+ type: "function"
1437
+ },
1438
+ {
1439
+ inputs: [
1440
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1441
+ { internalType: "uint256", name: "contentType", type: "uint256" },
1442
+ { internalType: "bytes", name: "data", type: "bytes" }
1443
+ ],
1444
+ name: "setABI",
1445
+ outputs: [],
1446
+ stateMutability: "nonpayable",
1447
+ type: "function"
1448
+ },
1449
+ {
1450
+ inputs: [
1451
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1452
+ { internalType: "uint256", name: "coinType", type: "uint256" },
1453
+ { internalType: "bytes", name: "a", type: "bytes" }
1454
+ ],
1455
+ name: "setAddr",
1456
+ outputs: [],
1457
+ stateMutability: "nonpayable",
1458
+ type: "function"
1459
+ },
1460
+ {
1461
+ inputs: [
1462
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1463
+ { internalType: "address", name: "a", type: "address" }
1464
+ ],
1465
+ name: "setAddr",
1466
+ outputs: [],
1467
+ stateMutability: "nonpayable",
1468
+ type: "function"
1469
+ },
1470
+ {
1471
+ inputs: [
1472
+ { internalType: "address", name: "operator", type: "address" },
1473
+ { internalType: "bool", name: "approved", type: "bool" }
1474
+ ],
1475
+ name: "setApprovalForAll",
1476
+ outputs: [],
1477
+ stateMutability: "nonpayable",
1478
+ type: "function"
1479
+ },
1480
+ {
1481
+ inputs: [
1482
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1483
+ { internalType: "bytes", name: "hash", type: "bytes" }
1484
+ ],
1485
+ name: "setContenthash",
1486
+ outputs: [],
1487
+ stateMutability: "nonpayable",
1488
+ type: "function"
1489
+ },
1490
+ {
1491
+ inputs: [
1492
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1493
+ { internalType: "bytes", name: "data", type: "bytes" }
1494
+ ],
1495
+ name: "setDNSRecords",
1496
+ outputs: [],
1497
+ stateMutability: "nonpayable",
1498
+ type: "function"
1499
+ },
1500
+ {
1501
+ inputs: [
1502
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1503
+ { internalType: "bytes4", name: "interfaceID", type: "bytes4" },
1504
+ { internalType: "address", name: "implementer", type: "address" }
1505
+ ],
1506
+ name: "setInterface",
1507
+ outputs: [],
1508
+ stateMutability: "nonpayable",
1509
+ type: "function"
1510
+ },
1511
+ {
1512
+ inputs: [
1513
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1514
+ { internalType: "string", name: "newName", type: "string" }
1515
+ ],
1516
+ name: "setName",
1517
+ outputs: [],
1518
+ stateMutability: "nonpayable",
1519
+ type: "function"
1520
+ },
1521
+ {
1522
+ inputs: [
1523
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1524
+ { internalType: "bytes32", name: "x", type: "bytes32" },
1525
+ { internalType: "bytes32", name: "y", type: "bytes32" }
1526
+ ],
1527
+ name: "setPubkey",
1528
+ outputs: [],
1529
+ stateMutability: "nonpayable",
1530
+ type: "function"
1531
+ },
1532
+ {
1533
+ inputs: [
1534
+ {
1535
+ internalType: "address",
1536
+ name: "registrarController_",
1537
+ type: "address"
1538
+ }
1539
+ ],
1540
+ name: "setRegistrarController",
1541
+ outputs: [],
1542
+ stateMutability: "nonpayable",
1543
+ type: "function"
1544
+ },
1545
+ {
1546
+ inputs: [
1547
+ { internalType: "address", name: "reverseRegistrar_", type: "address" }
1548
+ ],
1549
+ name: "setReverseRegistrar",
1550
+ outputs: [],
1551
+ stateMutability: "nonpayable",
1552
+ type: "function"
1553
+ },
1554
+ {
1555
+ inputs: [
1556
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1557
+ { internalType: "string", name: "key", type: "string" },
1558
+ { internalType: "string", name: "value", type: "string" }
1559
+ ],
1560
+ name: "setText",
1561
+ outputs: [],
1562
+ stateMutability: "nonpayable",
1563
+ type: "function"
1564
+ },
1565
+ {
1566
+ inputs: [
1567
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1568
+ { internalType: "bytes", name: "hash", type: "bytes" }
1569
+ ],
1570
+ name: "setZonehash",
1571
+ outputs: [],
1572
+ stateMutability: "nonpayable",
1573
+ type: "function"
1574
+ },
1575
+ {
1576
+ inputs: [{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }],
1577
+ name: "supportsInterface",
1578
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
1579
+ stateMutability: "view",
1580
+ type: "function"
1581
+ },
1582
+ {
1583
+ inputs: [
1584
+ { internalType: "bytes32", name: "node", type: "bytes32" },
1585
+ { internalType: "string", name: "key", type: "string" }
1586
+ ],
1587
+ name: "text",
1588
+ outputs: [{ internalType: "string", name: "", type: "string" }],
1589
+ stateMutability: "view",
1590
+ type: "function"
1591
+ },
1592
+ {
1593
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
1594
+ name: "transferOwnership",
1595
+ outputs: [],
1596
+ stateMutability: "payable",
1597
+ type: "function"
1598
+ },
1599
+ {
1600
+ inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
1601
+ name: "zonehash",
1602
+ outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
1603
+ stateMutability: "view",
1604
+ type: "function"
1605
+ }
1606
+ ];
1607
+
1608
+ // src/resolver/basename-resolver.ts
1609
+ var BASENAME_L2_RESOLVER_ADDRESS = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD";
1610
+ var BasenameTextRecordKeys = {
1611
+ Email: "email",
1612
+ Url: "url",
1613
+ Avatar: "avatar",
1614
+ Description: "description",
1615
+ Notice: "notice",
1616
+ Keywords: "keywords",
1617
+ Twitter: "com.twitter",
1618
+ Github: "com.github",
1619
+ Discord: "com.discord",
1620
+ Telegram: "org.telegram",
1621
+ Snapshot: "snapshot",
1622
+ Location: "location"
1623
+ };
1624
+ var convertChainIdToCoinType = (chainId) => {
1625
+ if (chainId === mainnet.id) {
1626
+ return "addr";
1627
+ }
1628
+ const cointype = (2147483648 | chainId) >>> 0;
1629
+ return cointype.toString(16).toLocaleUpperCase();
1630
+ };
1631
+ var convertReverseNodeToBytes = (address, chainId) => {
1632
+ const addressFormatted = address.toLocaleLowerCase();
1633
+ const addressNode = keccak256(addressFormatted.substring(2));
1634
+ const chainCoinType = convertChainIdToCoinType(chainId);
1635
+ const baseReverseNode = namehash(
1636
+ `${chainCoinType.toLocaleUpperCase()}.reverse`
1637
+ );
1638
+ const addressReverseNode = keccak256(
1639
+ encodePacked(["bytes32", "bytes32"], [baseReverseNode, addressNode])
1640
+ );
1641
+ return addressReverseNode;
1642
+ };
1643
+ function convertBasenameToNode(basename) {
1644
+ return namehash(basename);
1645
+ }
1646
+ function getResolverAddress() {
1647
+ const resolverAddress = BASENAME_L2_RESOLVER_ADDRESS;
1648
+ return resolverAddress;
1649
+ }
1650
+ var BasenameResolver = class {
1651
+ constructor(options) {
1652
+ __publicField(this, "cache", /* @__PURE__ */ new Map());
1653
+ __publicField(this, "textRecordCache", /* @__PURE__ */ new Map());
1654
+ __publicField(this, "maxCacheSize");
1655
+ __publicField(this, "cacheTtl");
1656
+ __publicField(this, "baseClient");
1657
+ __publicField(this, "resolverAddress", null);
1658
+ __publicField(this, "chainId", null);
1659
+ this.maxCacheSize = options.maxCacheSize ?? 500;
1660
+ this.cacheTtl = options.cacheTtl ?? 36e5;
1661
+ this.baseClient = options.publicClient;
1662
+ this.initializeResolver();
1663
+ }
1664
+ /**
1665
+ * Initialize the resolver address based on the client's chain ID
1666
+ */
1667
+ async initializeResolver() {
1668
+ if (this.resolverAddress && this.chainId) {
1669
+ console.log(
1670
+ `\u{1F504} BasenameResolver already initialized for chain ${this.chainId} with resolver ${this.resolverAddress}`
1671
+ );
1672
+ return;
1673
+ }
1674
+ try {
1675
+ console.log("\u{1F504} Initializing BasenameResolver...");
1676
+ this.chainId = await this.baseClient.getChainId();
1677
+ console.log(`\u{1F517} Chain ID detected: ${this.chainId}`);
1678
+ this.resolverAddress = getResolverAddress();
1679
+ console.log(
1680
+ `\u{1F4CD} Resolver address for chain ${this.chainId}: ${this.resolverAddress}`
1681
+ );
1682
+ console.log(
1683
+ `\u2705 Initialized BasenameResolver for chain ${this.chainId} with resolver ${this.resolverAddress}`
1684
+ );
1685
+ } catch (error) {
1686
+ console.error("\u274C Failed to initialize BasenameResolver:", error);
1687
+ throw error;
1688
+ }
1689
+ }
1690
+ /**
1691
+ * Get the resolver address, initializing if necessary
1692
+ */
1693
+ async getResolverAddress() {
1694
+ await this.initializeResolver();
1695
+ if (!this.resolverAddress) {
1696
+ throw new Error("Failed to initialize resolver address");
1697
+ }
1698
+ return this.resolverAddress;
1699
+ }
1700
+ /**
1701
+ * Resolve a basename from an Ethereum address
1702
+ */
1703
+ async getBasename(address) {
1704
+ console.log(`\u{1F50D} Starting basename resolution for address: ${address}`);
1705
+ try {
1706
+ const cached = this.getCachedBasename(address);
1707
+ if (cached) {
1708
+ console.log(`\u2705 Resolved basename from cache: ${cached}`);
1709
+ return cached;
1710
+ }
1711
+ console.log(`\u{1F4ED} No cached basename found for address: ${address}`);
1712
+ console.log("\u{1F504} Getting resolver address...");
1713
+ const resolverAddress = await this.getResolverAddress();
1714
+ console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
1715
+ console.log("\u{1F504} Getting chain ID...");
1716
+ const chainId = await this.baseClient.getChainId();
1717
+ console.log(`\u{1F517} Chain ID: ${chainId}`);
1718
+ console.log("\u{1F504} Converting address to reverse node...");
1719
+ const addressReverseNode = convertReverseNodeToBytes(
1720
+ // address.toUpperCase() as `0x${string}`,
1721
+ address,
1722
+ chainId
1723
+ );
1724
+ console.log(`\u{1F517} Reverse node: ${addressReverseNode}`);
1725
+ console.log("\u{1F504} Reading contract to resolve basename...");
1726
+ const basename = await this.baseClient.readContract({
1727
+ abi: L2ResolverAbi,
1728
+ address: resolverAddress,
1729
+ functionName: "name",
1730
+ args: [addressReverseNode]
1731
+ });
1732
+ console.log(
1733
+ `\u{1F4CB} Contract returned basename: "${basename}" (length: ${basename?.length || 0})`
1734
+ );
1735
+ if (basename && basename.length > 0) {
1736
+ this.setCachedBasename(address, basename);
1737
+ console.log(`\u2705 Resolved basename: ${basename} for address: ${address}`);
1738
+ return basename;
1739
+ }
1740
+ console.log(
1741
+ `\u274C No basename found for address: ${address} (empty or null response)`
1742
+ );
1743
+ return null;
1744
+ } catch (error) {
1745
+ console.error(
1746
+ `\u274C Error resolving basename for address ${address}:`,
1747
+ error
1748
+ );
1749
+ if (error instanceof Error) {
1750
+ console.error(`\u274C Error details: ${error.message}`);
1751
+ console.error(`\u274C Error stack:`, error.stack);
1752
+ }
1753
+ return null;
1754
+ }
1755
+ }
1756
+ /**
1757
+ * Get the avatar URL for a basename
1758
+ */
1759
+ async getBasenameAvatar(basename) {
1760
+ console.log(`\u{1F5BC}\uFE0F Getting avatar for basename: ${basename}`);
1761
+ return this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Avatar);
1762
+ }
1763
+ /**
1764
+ * Get a text record for a basename
1765
+ */
1766
+ async getBasenameTextRecord(basename, key) {
1767
+ console.log(`\u{1F4DD} Getting text record "${key}" for basename: ${basename}`);
1768
+ try {
1769
+ const cached = this.getCachedTextRecord(basename, key);
1770
+ if (cached) {
1771
+ console.log(`\u2705 Resolved text record from cache: ${key}=${cached}`);
1772
+ return cached;
1773
+ }
1774
+ console.log(`\u{1F4ED} No cached text record found for ${basename}.${key}`);
1775
+ console.log("\u{1F504} Getting resolver address...");
1776
+ const resolverAddress = await this.getResolverAddress();
1777
+ console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
1778
+ console.log("\u{1F504} Converting basename to node...");
1779
+ const node = convertBasenameToNode(basename);
1780
+ console.log(`\u{1F517} Node hash: ${node}`);
1781
+ console.log(`\u{1F504} Reading contract for text record "${key}"...`);
1782
+ const textRecord = await this.baseClient.readContract({
1783
+ abi: L2ResolverAbi,
1784
+ address: resolverAddress,
1785
+ functionName: "text",
1786
+ args: [node, key]
1787
+ });
1788
+ console.log(
1789
+ `\u{1F4CB} Contract returned text record: "${textRecord}" (length: ${textRecord?.length || 0})`
1790
+ );
1791
+ if (textRecord && textRecord.length > 0) {
1792
+ this.setCachedTextRecord(basename, key, textRecord);
1793
+ console.log(`\u2705 Resolved text record: ${key}=${textRecord}`);
1794
+ return textRecord;
1795
+ }
1796
+ console.log(
1797
+ `\u274C No text record found for ${basename}.${key} (empty or null response)`
1798
+ );
1799
+ return null;
1800
+ } catch (error) {
1801
+ console.error(
1802
+ `\u274C Error resolving text record ${key} for ${basename}:`,
1803
+ error
1804
+ );
1805
+ if (error instanceof Error) {
1806
+ console.error(`\u274C Error details: ${error.message}`);
1807
+ console.error(`\u274C Error stack:`, error.stack);
1808
+ }
1809
+ return null;
1810
+ }
1811
+ }
1812
+ /**
1813
+ * Get the Ethereum address that owns a basename
1814
+ */
1815
+ async getBasenameAddress(basename) {
1816
+ console.log(`\u{1F50D} Getting address for basename: ${basename}`);
1817
+ try {
1818
+ console.log("\u{1F504} Getting resolver address...");
1819
+ const resolverAddress = await this.getResolverAddress();
1820
+ console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
1821
+ console.log("\u{1F504} Converting basename to node...");
1822
+ const node = convertBasenameToNode(basename);
1823
+ console.log(`\u{1F517} Node hash: ${node}`);
1824
+ console.log("\u{1F504} Reading contract to resolve address...");
1825
+ const address = await this.baseClient.readContract({
1826
+ abi: L2ResolverAbi,
1827
+ address: resolverAddress,
1828
+ functionName: "addr",
1829
+ args: [node]
1830
+ });
1831
+ console.log(`\u{1F4CB} Contract returned address: "${address}"`);
1832
+ if (address && address !== "0x0000000000000000000000000000000000000000") {
1833
+ console.log(`\u2705 Resolved address: ${address} for basename: ${basename}`);
1834
+ return address;
1835
+ }
1836
+ console.log(
1837
+ `\u274C No address found for basename: ${basename} (zero address or null response)`
1838
+ );
1839
+ return null;
1840
+ } catch (error) {
1841
+ console.error(
1842
+ `\u274C Error resolving address for basename ${basename}:`,
1843
+ error
1844
+ );
1845
+ if (error instanceof Error) {
1846
+ console.error(`\u274C Error details: ${error.message}`);
1847
+ console.error(`\u274C Error stack:`, error.stack);
1848
+ }
1849
+ return null;
1850
+ }
1851
+ }
1852
+ /**
1853
+ * Get all basic metadata for a basename
1854
+ */
1855
+ async getBasenameMetadata(basename) {
1856
+ console.log(`\u{1F4CA} Getting metadata for basename: ${basename}`);
1857
+ try {
1858
+ const [avatar, description, twitter, github, url] = await Promise.all([
1859
+ this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Avatar),
1860
+ this.getBasenameTextRecord(
1861
+ basename,
1862
+ BasenameTextRecordKeys.Description
1863
+ ),
1864
+ this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Twitter),
1865
+ this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Github),
1866
+ this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Url)
1867
+ ]);
1868
+ const metadata = {
1869
+ basename,
1870
+ avatar,
1871
+ description,
1872
+ twitter,
1873
+ github,
1874
+ url
1875
+ };
1876
+ console.log(`\u2705 Resolved metadata for ${basename}:`, metadata);
1877
+ return metadata;
1878
+ } catch (error) {
1879
+ console.error(
1880
+ `\u274C Error resolving metadata for basename ${basename}:`,
1881
+ error
1882
+ );
1883
+ if (error instanceof Error) {
1884
+ console.error(`\u274C Error details: ${error.message}`);
1885
+ console.error(`\u274C Error stack:`, error.stack);
1886
+ }
1887
+ return null;
1888
+ }
1889
+ }
1890
+ /**
1891
+ * Resolve a full basename profile (name + metadata) from an address
1892
+ */
1893
+ async resolveBasenameProfile(address) {
1894
+ console.log(`\u{1F464} Resolving full basename profile for address: ${address}`);
1895
+ try {
1896
+ const basename = await this.getBasename(address);
1897
+ if (!basename) {
1898
+ console.log(`\u274C No basename found for address: ${address}`);
1899
+ return null;
1900
+ }
1901
+ console.log(`\u{1F504} Getting metadata for resolved basename: ${basename}`);
1902
+ const metadata = await this.getBasenameMetadata(basename);
1903
+ const profile = {
1904
+ address,
1905
+ ...metadata
1906
+ };
1907
+ console.log(`\u2705 Resolved full profile for ${address}:`, profile);
1908
+ return profile;
1909
+ } catch (error) {
1910
+ console.error(
1911
+ `\u274C Error resolving basename profile for ${address}:`,
1912
+ error
1913
+ );
1914
+ if (error instanceof Error) {
1915
+ console.error(`\u274C Error details: ${error.message}`);
1916
+ console.error(`\u274C Error stack:`, error.stack);
1917
+ }
1918
+ return null;
1919
+ }
1920
+ }
1921
+ /**
1922
+ * Get cached basename if not expired
1923
+ */
1924
+ getCachedBasename(address) {
1925
+ const entry = this.cache.get(address.toLowerCase());
1926
+ if (!entry) {
1927
+ console.log(
1928
+ `\u{1F4ED} No cache entry found for address: ${address.toLowerCase()}`
1929
+ );
1930
+ return null;
1931
+ }
1932
+ const now = Date.now();
1933
+ const age = now - entry.timestamp;
1934
+ console.log(`\u{1F550} Cache entry age: ${age}ms (TTL: ${this.cacheTtl}ms)`);
1935
+ if (age > this.cacheTtl) {
1936
+ console.log(
1937
+ `\u23F0 Cache entry expired for address: ${address.toLowerCase()}`
1938
+ );
1939
+ this.cache.delete(address.toLowerCase());
1940
+ return null;
1941
+ }
1942
+ console.log(
1943
+ `\u2705 Valid cache entry found for ${address.toLowerCase()}: "${entry.basename}"`
1944
+ );
1945
+ return entry.basename;
1946
+ }
1947
+ /**
1948
+ * Cache basename with LRU eviction
1949
+ */
1950
+ setCachedBasename(address, basename) {
1951
+ if (this.cache.size >= this.maxCacheSize) {
1952
+ const firstKey = this.cache.keys().next().value;
1953
+ if (firstKey) {
1954
+ console.log(`\u{1F5D1}\uFE0F Cache full, removing oldest entry: ${firstKey}`);
1955
+ this.cache.delete(firstKey);
1956
+ }
1957
+ }
1958
+ console.log(
1959
+ `\u{1F4BE} Caching basename "${basename}" for address: ${address.toLowerCase()}`
1960
+ );
1961
+ this.cache.set(address.toLowerCase(), {
1962
+ basename,
1963
+ timestamp: Date.now()
1964
+ });
1965
+ }
1966
+ /**
1967
+ * Get cached text record if not expired
1968
+ */
1969
+ getCachedTextRecord(basename, key) {
1970
+ const basenameCache = this.textRecordCache.get(basename);
1971
+ if (!basenameCache) {
1972
+ console.log(`\u{1F4ED} No text record cache found for basename: ${basename}`);
1973
+ return null;
1974
+ }
1975
+ const entry = basenameCache.get(key);
1976
+ if (!entry) {
1977
+ console.log(`\u{1F4ED} No cached text record found for ${basename}.${key}`);
1978
+ return null;
1979
+ }
1980
+ const now = Date.now();
1981
+ const age = now - entry.timestamp;
1982
+ console.log(
1983
+ `\u{1F550} Text record cache entry age: ${age}ms (TTL: ${this.cacheTtl}ms)`
1984
+ );
1985
+ if (age > this.cacheTtl) {
1986
+ console.log(`\u23F0 Text record cache entry expired for ${basename}.${key}`);
1987
+ basenameCache.delete(key);
1988
+ return null;
1989
+ }
1990
+ console.log(
1991
+ `\u2705 Valid text record cache entry found for ${basename}.${key}: "${entry.value}"`
1992
+ );
1993
+ return entry.value;
1994
+ }
1995
+ /**
1996
+ * Cache text record
1997
+ */
1998
+ setCachedTextRecord(basename, key, value) {
1999
+ let basenameCache = this.textRecordCache.get(basename);
2000
+ if (!basenameCache) {
2001
+ console.log(`\u{1F4DD} Creating new text record cache for basename: ${basename}`);
2002
+ basenameCache = /* @__PURE__ */ new Map();
2003
+ this.textRecordCache.set(basename, basenameCache);
2004
+ }
2005
+ console.log(
2006
+ `\u{1F4BE} Caching text record "${key}" = "${value}" for basename: ${basename}`
2007
+ );
2008
+ basenameCache.set(key, {
2009
+ value,
2010
+ timestamp: Date.now()
2011
+ });
2012
+ }
2013
+ /**
2014
+ * Clear all caches
2015
+ */
2016
+ clearCache() {
2017
+ const basenameCount = this.cache.size;
2018
+ const textRecordCount = this.textRecordCache.size;
2019
+ this.cache.clear();
2020
+ this.textRecordCache.clear();
2021
+ console.log(`\u{1F5D1}\uFE0F Basename cache cleared (${basenameCount} entries removed)`);
2022
+ console.log(
2023
+ `\u{1F5D1}\uFE0F Text record cache cleared (${textRecordCount} basename caches removed)`
2024
+ );
2025
+ }
2026
+ /**
2027
+ * Get cache statistics
2028
+ */
2029
+ getCacheStats() {
2030
+ return {
2031
+ basenameCache: {
2032
+ size: this.cache.size,
2033
+ maxSize: this.maxCacheSize
2034
+ },
2035
+ textRecordCache: {
2036
+ size: this.textRecordCache.size
2037
+ },
2038
+ chainId: this.chainId,
2039
+ resolverAddress: this.resolverAddress
2040
+ };
2041
+ }
2042
+ };
2043
+
2044
+ // src/resolver/ens-resolver.ts
2045
+ var ENSResolver = class {
2046
+ constructor(options) {
2047
+ __publicField(this, "cache", /* @__PURE__ */ new Map());
2048
+ __publicField(this, "reverseCache", /* @__PURE__ */ new Map());
2049
+ __publicField(this, "maxCacheSize");
2050
+ __publicField(this, "cacheTtl");
2051
+ __publicField(this, "mainnetClient");
2052
+ this.maxCacheSize = options.maxCacheSize ?? 500;
2053
+ this.cacheTtl = options.cacheTtl ?? 36e5;
2054
+ this.mainnetClient = options.mainnetClient;
2055
+ }
2056
+ /**
2057
+ * Resolve an ENS name to an Ethereum address
2058
+ */
2059
+ async resolveENSName(ensName) {
2060
+ console.log(`\u{1F50D} Resolving ENS name: ${ensName}`);
2061
+ try {
2062
+ const cached = this.getCachedAddress(ensName);
2063
+ if (cached) {
2064
+ console.log(`\u2705 Resolved ENS from cache: ${ensName} \u2192 ${cached}`);
2065
+ return cached;
2066
+ }
2067
+ console.log(`\u{1F4ED} No cached address found for ENS: ${ensName}`);
2068
+ console.log("\u{1F504} Reading ENS contract...");
2069
+ const address = await this.mainnetClient.getEnsAddress({
2070
+ name: ensName
2071
+ });
2072
+ console.log(`\u{1F4CB} ENS contract returned address: "${address}"`);
2073
+ if (address && address !== "0x0000000000000000000000000000000000000000") {
2074
+ this.setCachedAddress(ensName, address);
2075
+ console.log(`\u2705 Resolved ENS: ${ensName} \u2192 ${address}`);
2076
+ return address;
2077
+ }
2078
+ console.log(`\u274C No address found for ENS: ${ensName}`);
2079
+ return null;
2080
+ } catch (error) {
2081
+ console.error(`\u274C Error resolving ENS name ${ensName}:`, error);
2082
+ if (error instanceof Error) {
2083
+ console.error(`\u274C Error details: ${error.message}`);
2084
+ }
2085
+ return null;
2086
+ }
2087
+ }
2088
+ /**
2089
+ * Resolve an address to its primary ENS name (reverse resolution)
2090
+ */
2091
+ async resolveAddressToENS(address) {
2092
+ console.log(`\u{1F50D} Reverse resolving address to ENS: ${address}`);
2093
+ try {
2094
+ const cached = this.getCachedENSName(address);
2095
+ if (cached) {
2096
+ console.log(
2097
+ `\u2705 Resolved ENS from reverse cache: ${address} \u2192 ${cached}`
2098
+ );
2099
+ return cached;
2100
+ }
2101
+ console.log(`\u{1F4ED} No cached ENS name found for address: ${address}`);
2102
+ console.log("\u{1F504} Reading ENS reverse resolver...");
2103
+ const ensName = await this.mainnetClient.getEnsName({
2104
+ address
2105
+ });
2106
+ console.log(`\u{1F4CB} ENS reverse resolver returned: "${ensName}"`);
2107
+ if (ensName && ensName.length > 0) {
2108
+ this.setCachedENSName(address, ensName);
2109
+ console.log(`\u2705 Reverse resolved: ${address} \u2192 ${ensName}`);
2110
+ return ensName;
2111
+ }
2112
+ console.log(`\u274C No ENS name found for address: ${address}`);
2113
+ return null;
2114
+ } catch (error) {
2115
+ console.error(`\u274C Error reverse resolving address ${address}:`, error);
2116
+ if (error instanceof Error) {
2117
+ console.error(`\u274C Error details: ${error.message}`);
2118
+ }
2119
+ return null;
2120
+ }
2121
+ }
2122
+ /**
2123
+ * Get ENS avatar for a name
2124
+ */
2125
+ async getENSAvatar(ensName) {
2126
+ console.log(`\u{1F5BC}\uFE0F Getting ENS avatar for: ${ensName}`);
2127
+ try {
2128
+ const avatar = await this.mainnetClient.getEnsAvatar({
2129
+ name: ensName
2130
+ });
2131
+ if (avatar) {
2132
+ console.log(`\u2705 Found ENS avatar: ${avatar}`);
2133
+ return avatar;
2134
+ }
2135
+ console.log(`\u274C No avatar found for ENS: ${ensName}`);
2136
+ return null;
2137
+ } catch (error) {
2138
+ console.error(`\u274C Error getting ENS avatar for ${ensName}:`, error);
2139
+ return null;
2140
+ }
2141
+ }
2142
+ /**
2143
+ * Get ENS text record
2144
+ */
2145
+ async getENSTextRecord(ensName, key) {
2146
+ console.log(`\u{1F4DD} Getting ENS text record "${key}" for: ${ensName}`);
2147
+ try {
2148
+ const textRecord = await this.mainnetClient.getEnsText({
2149
+ name: ensName,
2150
+ key
2151
+ });
2152
+ if (textRecord && textRecord.length > 0) {
2153
+ console.log(`\u2705 Found ENS text record: ${key}=${textRecord}`);
2154
+ return textRecord;
2155
+ }
2156
+ console.log(`\u274C No text record "${key}" found for ENS: ${ensName}`);
2157
+ return null;
2158
+ } catch (error) {
2159
+ console.error(
2160
+ `\u274C Error getting ENS text record ${key} for ${ensName}:`,
2161
+ error
2162
+ );
2163
+ return null;
2164
+ }
2165
+ }
2166
+ /**
2167
+ * Get comprehensive ENS profile
2168
+ */
2169
+ async getENSProfile(ensName) {
2170
+ console.log(`\u{1F464} Getting ENS profile for: ${ensName}`);
2171
+ try {
2172
+ const [address, avatar, description, twitter, github, url] = await Promise.all([
2173
+ this.resolveENSName(ensName),
2174
+ this.getENSAvatar(ensName),
2175
+ this.getENSTextRecord(ensName, "description"),
2176
+ this.getENSTextRecord(ensName, "com.twitter"),
2177
+ this.getENSTextRecord(ensName, "com.github"),
2178
+ this.getENSTextRecord(ensName, "url")
2179
+ ]);
2180
+ const profile = {
2181
+ ensName,
2182
+ address,
2183
+ avatar,
2184
+ description,
2185
+ twitter,
2186
+ github,
2187
+ url
2188
+ };
2189
+ console.log(`\u2705 ENS profile for ${ensName}:`, profile);
2190
+ return profile;
2191
+ } catch (error) {
2192
+ console.error(`\u274C Error getting ENS profile for ${ensName}:`, error);
2193
+ return null;
2194
+ }
2195
+ }
2196
+ /**
2197
+ * Check if a name is a valid ENS name (.eth)
2198
+ */
2199
+ isENSName(name) {
2200
+ return name.endsWith(".eth") && !name.endsWith(".base.eth");
2201
+ }
2202
+ /**
2203
+ * Get cached address if not expired
2204
+ */
2205
+ getCachedAddress(ensName) {
2206
+ const entry = this.cache.get(ensName.toLowerCase());
2207
+ if (!entry) {
2208
+ return null;
2209
+ }
2210
+ const now = Date.now();
2211
+ if (now - entry.timestamp > this.cacheTtl) {
2212
+ this.cache.delete(ensName.toLowerCase());
2213
+ return null;
2214
+ }
2215
+ return entry.address;
2216
+ }
2217
+ /**
2218
+ * Cache address with LRU eviction
2219
+ */
2220
+ setCachedAddress(ensName, address) {
2221
+ if (this.cache.size >= this.maxCacheSize) {
2222
+ const firstKey = this.cache.keys().next().value;
2223
+ if (firstKey) {
2224
+ this.cache.delete(firstKey);
2225
+ }
2226
+ }
2227
+ this.cache.set(ensName.toLowerCase(), {
2228
+ address,
2229
+ timestamp: Date.now()
2230
+ });
2231
+ }
2232
+ /**
2233
+ * Get cached ENS name if not expired
2234
+ */
2235
+ getCachedENSName(address) {
2236
+ const entry = this.reverseCache.get(address.toLowerCase());
2237
+ if (!entry) {
2238
+ return null;
2239
+ }
2240
+ const now = Date.now();
2241
+ if (now - entry.timestamp > this.cacheTtl) {
2242
+ this.reverseCache.delete(address.toLowerCase());
2243
+ return null;
2244
+ }
2245
+ return entry.ensName;
2246
+ }
2247
+ /**
2248
+ * Cache ENS name with LRU eviction
2249
+ */
2250
+ setCachedENSName(address, ensName) {
2251
+ if (this.reverseCache.size >= this.maxCacheSize) {
2252
+ const firstKey = this.reverseCache.keys().next().value;
2253
+ if (firstKey) {
2254
+ this.reverseCache.delete(firstKey);
2255
+ }
2256
+ }
2257
+ this.reverseCache.set(address.toLowerCase(), {
2258
+ ensName,
2259
+ timestamp: Date.now()
2260
+ });
2261
+ }
2262
+ /**
2263
+ * Clear all caches
2264
+ */
2265
+ clearCache() {
2266
+ const addressCount = this.cache.size;
2267
+ const ensCount = this.reverseCache.size;
2268
+ this.cache.clear();
2269
+ this.reverseCache.clear();
2270
+ console.log(`\u{1F5D1}\uFE0F ENS address cache cleared (${addressCount} entries removed)`);
2271
+ console.log(`\u{1F5D1}\uFE0F ENS reverse cache cleared (${ensCount} entries removed)`);
2272
+ }
2273
+ /**
2274
+ * Get cache statistics
2275
+ */
2276
+ getCacheStats() {
2277
+ return {
2278
+ addressCache: {
2279
+ size: this.cache.size,
2280
+ maxSize: this.maxCacheSize
2281
+ },
2282
+ reverseCache: {
2283
+ size: this.reverseCache.size,
2284
+ maxSize: this.maxCacheSize
2285
+ }
2286
+ };
2287
+ }
2288
+ };
2289
+
2290
+ // src/resolver/xmtp-resolver.ts
2291
+ var XmtpResolver = class {
2292
+ constructor(client, options = {}) {
2293
+ this.client = client;
2294
+ __publicField(this, "addressCache", /* @__PURE__ */ new Map());
2295
+ __publicField(this, "messageCache", /* @__PURE__ */ new Map());
2296
+ __publicField(this, "maxCacheSize");
2297
+ __publicField(this, "cacheTtl");
2298
+ __publicField(this, "maxMessageCacheSize");
2299
+ __publicField(this, "messageCacheTtl");
2300
+ this.maxCacheSize = options.maxCacheSize ?? 1e3;
2301
+ this.cacheTtl = options.cacheTtl ?? 864e5;
2302
+ this.maxMessageCacheSize = options.maxMessageCacheSize ?? 1e3;
2303
+ this.messageCacheTtl = options.messageCacheTtl ?? 36e5;
2304
+ }
2305
+ /**
2306
+ * Resolve user address from inbox ID with caching
2307
+ */
2308
+ async resolveAddress(inboxId, conversationId) {
2309
+ const cached = this.getCachedAddress(inboxId);
2310
+ if (cached) {
2311
+ console.log(
2312
+ `\u2705 [XmtpResolver] Resolved user address from cache: ${cached}`
2313
+ );
2314
+ return cached;
2315
+ }
2316
+ let userAddress = void 0;
2317
+ try {
2318
+ if (conversationId) {
2319
+ const conversation = await this.client.conversations.getConversationById(conversationId);
2320
+ if (conversation) {
2321
+ userAddress = await this.resolveFromConversation(
2322
+ conversation,
2323
+ inboxId
2324
+ );
2325
+ if (userAddress) {
2326
+ this.setCachedAddress(inboxId, userAddress);
2327
+ console.log(
2328
+ `\u2705 [XmtpResolver] Resolved user address: ${userAddress}`
2329
+ );
2330
+ return userAddress;
2331
+ }
2332
+ }
2333
+ }
2334
+ userAddress = await this.resolveFromInboxState(inboxId);
2335
+ if (userAddress) {
2336
+ this.setCachedAddress(inboxId, userAddress);
2337
+ console.log(
2338
+ `\u2705 [XmtpResolver] Resolved user address via fallback: ${userAddress}`
2339
+ );
2340
+ return userAddress;
2341
+ }
2342
+ console.log(`\u26A0\uFE0F [XmtpResolver] No identifiers found for inbox ${inboxId}`);
2343
+ return null;
2344
+ } catch (error) {
2345
+ console.error(
2346
+ `\u274C [XmtpResolver] Error resolving user address for ${inboxId}:`,
2347
+ error
2348
+ );
2349
+ return null;
2350
+ }
2351
+ }
2352
+ /**
2353
+ * Find any message by ID with caching
2354
+ */
2355
+ async findMessage(messageId) {
2356
+ const cached = this.getCachedMessage(messageId);
2357
+ if (cached !== void 0) {
2358
+ console.log(
2359
+ cached ? `\u2705 [XmtpResolver] Found message from cache: ${cached.id}` : `\u2705 [XmtpResolver] Found cached null message for: ${messageId}`
2360
+ );
2361
+ return cached;
2362
+ }
2363
+ try {
2364
+ console.log(`\u{1F50D} [XmtpResolver] Finding message: ${messageId}`);
2365
+ const message = await this.client.conversations.getMessageById(messageId);
2366
+ if (message) {
2367
+ this.setCachedMessage(messageId, message);
2368
+ console.log(`\u2705 [XmtpResolver] Found and cached message: ${message.id}`);
2369
+ return message;
2370
+ }
2371
+ console.log(`\u26A0\uFE0F [XmtpResolver] Message not found: ${messageId}`);
2372
+ this.setCachedMessage(messageId, null);
2373
+ return null;
2374
+ } catch (error) {
2375
+ console.error(
2376
+ `\u274C [XmtpResolver] Error finding message ${messageId}:`,
2377
+ error
2378
+ );
2379
+ this.setCachedMessage(messageId, null);
2380
+ return null;
2381
+ }
2382
+ }
2383
+ /**
2384
+ * Find root message with caching
2385
+ */
2386
+ async findRootMessage(messageId) {
2387
+ const rootCacheKey = `root:${messageId}`;
2388
+ const cached = this.getCachedMessage(rootCacheKey);
2389
+ if (cached !== void 0) {
2390
+ console.log(
2391
+ cached ? `\u2705 [XmtpResolver] Found root message from cache: ${cached.id}` : `\u2705 [XmtpResolver] Found cached null root for: ${messageId}`
2392
+ );
2393
+ return cached;
2394
+ }
2395
+ try {
2396
+ console.log(`\u{1F50D} [XmtpResolver] Finding root message for: ${messageId}`);
2397
+ const rootMessage = await this.findRootMessageRecursive(messageId);
2398
+ if (rootMessage) {
2399
+ this.setCachedMessage(rootCacheKey, rootMessage);
2400
+ console.log(
2401
+ `\u2705 [XmtpResolver] Found and cached root message: ${rootMessage.id}`
2402
+ );
2403
+ return rootMessage;
2404
+ }
2405
+ console.log(`\u26A0\uFE0F [XmtpResolver] No root message found for: ${messageId}`);
2406
+ this.setCachedMessage(rootCacheKey, null);
2407
+ return null;
2408
+ } catch (error) {
2409
+ console.error(
2410
+ `\u274C [XmtpResolver] Error finding root message for ${messageId}:`,
2411
+ error
2412
+ );
2413
+ this.setCachedMessage(rootCacheKey, null);
2414
+ return null;
2415
+ }
2416
+ }
2417
+ /**
2418
+ * Recursively finds the root message in a reply chain by following reply references
2419
+ */
2420
+ async findRootMessageRecursive(messageId, visitedIds = /* @__PURE__ */ new Set()) {
2421
+ if (visitedIds.has(messageId)) {
2422
+ console.warn(
2423
+ `\u26A0\uFE0F Circular reference detected in message chain at ${messageId}`
2424
+ );
2425
+ return null;
2426
+ }
2427
+ visitedIds.add(messageId);
2428
+ const message = await this.client.conversations.getMessageById(messageId);
2429
+ if (!message) {
2430
+ console.warn(`\u26A0\uFE0F [findRootMessage] Message not found: ${messageId}`);
2431
+ return null;
2432
+ }
2433
+ console.log(`\u{1F50D} [findRootMessage] Raw message ${messageId}:`, {
2434
+ id: message.id,
2435
+ contentType: message.contentType,
2436
+ content: message.content,
2437
+ sentAt: message.sentAt
2438
+ });
2439
+ if (message.content?.reference) {
2440
+ return this.findRootMessageRecursive(
2441
+ message.content.reference,
2442
+ visitedIds
2443
+ );
2444
+ }
2445
+ return message;
2446
+ }
2447
+ /**
2448
+ * Resolve address from conversation members
2449
+ */
2450
+ async resolveFromConversation(conversation, inboxId) {
2451
+ try {
2452
+ const members = await conversation.members();
2453
+ const sender = members.find(
2454
+ (member) => member.inboxId.toLowerCase() === inboxId.toLowerCase()
2455
+ );
2456
+ if (sender) {
2457
+ const ethIdentifier = sender.accountIdentifiers.find(
2458
+ (id) => id.identifierKind === 0
2459
+ // IdentifierKind.Ethereum
2460
+ );
2461
+ if (ethIdentifier) {
2462
+ return ethIdentifier.identifier;
2463
+ } else {
2464
+ console.log(
2465
+ `\u26A0\uFE0F [XmtpResolver] No Ethereum identifier found for inbox ${inboxId}`
2466
+ );
2467
+ }
2468
+ } else {
2469
+ console.log(
2470
+ `\u26A0\uFE0F [XmtpResolver] Sender not found in conversation members for inbox ${inboxId}`
2471
+ );
2472
+ }
2473
+ } catch (error) {
2474
+ console.error(
2475
+ `\u274C [XmtpResolver] Error resolving from conversation members:`,
2476
+ error
2477
+ );
2478
+ }
2479
+ return null;
2480
+ }
2481
+ /**
2482
+ * Resolve address from inbox state (network fallback)
2483
+ */
2484
+ async resolveFromInboxState(inboxId) {
2485
+ try {
2486
+ const inboxState = await this.client.preferences.inboxStateFromInboxIds([
2487
+ inboxId
2488
+ ]);
2489
+ const firstState = inboxState?.[0];
2490
+ if (firstState?.identifiers && firstState.identifiers.length > 0) {
2491
+ const firstIdentifier = firstState.identifiers[0];
2492
+ return firstIdentifier?.identifier;
2493
+ }
2494
+ } catch (error) {
2495
+ console.error(
2496
+ `\u274C [XmtpResolver] Error resolving from inbox state:`,
2497
+ error
2498
+ );
2499
+ }
2500
+ return null;
2501
+ }
2502
+ /**
2503
+ * Get cached address if not expired
2504
+ */
2505
+ getCachedAddress(inboxId) {
2506
+ const entry = this.addressCache.get(inboxId);
2507
+ if (!entry) return null;
2508
+ const now = Date.now();
2509
+ if (now - entry.timestamp > this.cacheTtl) {
2510
+ this.addressCache.delete(inboxId);
2511
+ return null;
2512
+ }
2513
+ return entry.address;
2514
+ }
2515
+ /**
2516
+ * Cache address with LRU eviction
2517
+ */
2518
+ setCachedAddress(inboxId, address) {
2519
+ if (this.addressCache.size >= this.maxCacheSize) {
2520
+ const firstKey = this.addressCache.keys().next().value;
2521
+ if (firstKey) {
2522
+ this.addressCache.delete(firstKey);
2523
+ }
2524
+ }
2525
+ this.addressCache.set(inboxId, {
2526
+ address,
2527
+ timestamp: Date.now()
2528
+ });
2529
+ }
2530
+ /**
2531
+ * Get cached message if not expired
2532
+ */
2533
+ getCachedMessage(messageId) {
2534
+ const entry = this.messageCache.get(messageId);
2535
+ if (!entry) return void 0;
2536
+ const now = Date.now();
2537
+ if (now - entry.timestamp > this.messageCacheTtl) {
2538
+ this.messageCache.delete(messageId);
2539
+ return void 0;
2540
+ }
2541
+ return entry.message;
2542
+ }
2543
+ /**
2544
+ * Cache message with LRU eviction
2545
+ */
2546
+ setCachedMessage(messageId, message) {
2547
+ if (this.messageCache.size >= this.maxMessageCacheSize) {
2548
+ const firstKey = this.messageCache.keys().next().value;
2549
+ if (firstKey) {
2550
+ this.messageCache.delete(firstKey);
2551
+ }
2552
+ }
2553
+ this.messageCache.set(messageId, {
2554
+ message,
2555
+ timestamp: Date.now()
2556
+ });
2557
+ }
2558
+ /**
2559
+ * Pre-populate address cache from existing conversations
2560
+ */
2561
+ async prePopulateCache() {
2562
+ console.log("\u{1F504} [XmtpResolver] Pre-populating address cache...");
2563
+ try {
2564
+ const conversations = await this.client.conversations.list();
2565
+ let cachedCount = 0;
2566
+ for (const conversation of conversations) {
2567
+ try {
2568
+ const members = await conversation.members();
2569
+ for (const member of members) {
2570
+ const ethIdentifier = member.accountIdentifiers.find(
2571
+ (id) => id.identifierKind === 0
2572
+ // IdentifierKind.Ethereum
2573
+ );
2574
+ if (ethIdentifier) {
2575
+ this.setCachedAddress(
2576
+ member.inboxId,
2577
+ ethIdentifier.identifier
2578
+ );
2579
+ cachedCount++;
2580
+ }
2581
+ }
2582
+ } catch (error) {
2583
+ console.error(
2584
+ "[XmtpResolver] Error pre-caching conversation members:",
2585
+ error
2586
+ );
2587
+ }
2588
+ }
2589
+ console.log(
2590
+ `\u2705 [XmtpResolver] Pre-cached ${cachedCount} address mappings`
2591
+ );
2592
+ } catch (error) {
2593
+ console.error("[XmtpResolver] Error pre-populating cache:", error);
2594
+ }
2595
+ }
2596
+ /**
2597
+ * Clear all caches
2598
+ */
2599
+ clearCache() {
2600
+ this.addressCache.clear();
2601
+ this.messageCache.clear();
2602
+ console.log("\u{1F5D1}\uFE0F [XmtpResolver] All caches cleared");
2603
+ }
2604
+ /**
2605
+ * Get cache statistics
2606
+ */
2607
+ getCacheStats() {
2608
+ return {
2609
+ address: {
2610
+ size: this.addressCache.size,
2611
+ maxSize: this.maxCacheSize
2612
+ },
2613
+ message: {
2614
+ size: this.messageCache.size,
2615
+ maxSize: this.maxMessageCacheSize
2616
+ }
2617
+ };
2618
+ }
2619
+ };
2620
+
2621
+ // src/resolver/resolver.ts
2622
+ var Resolver = class {
2623
+ constructor(options) {
2624
+ __publicField(this, "addressResolver");
2625
+ __publicField(this, "ensResolver");
2626
+ __publicField(this, "basenameResolver");
2627
+ __publicField(this, "xmtpResolver");
2628
+ const resolverOptions = {
2629
+ maxCacheSize: options.maxCacheSize ?? 1e3,
2630
+ cacheTtl: options.cacheTtl ?? 36e5
2631
+ };
2632
+ this.addressResolver = new AddressResolver(
2633
+ options.xmtpClient,
2634
+ resolverOptions
2635
+ );
2636
+ this.xmtpResolver = new XmtpResolver(options.xmtpClient, resolverOptions);
2637
+ this.ensResolver = new ENSResolver({
2638
+ ...resolverOptions,
2639
+ mainnetClient: options.mainnetClient
2640
+ });
2641
+ this.basenameResolver = new BasenameResolver({
2642
+ ...resolverOptions,
2643
+ publicClient: options.baseClient
2644
+ });
2645
+ }
2646
+ // === Address Resolution Methods ===
2647
+ /**
2648
+ * Resolve user address from inbox ID with caching
2649
+ * Uses both AddressResolver and XmtpResolver for redundancy
2650
+ */
2651
+ async resolveAddress(inboxId, conversationId) {
2652
+ let result = await this.addressResolver.resolveAddress(
2653
+ inboxId,
2654
+ conversationId
2655
+ );
2656
+ if (!result) {
2657
+ result = await this.xmtpResolver.resolveAddress(inboxId, conversationId);
2658
+ }
2659
+ return result;
2660
+ }
2661
+ // === ENS Resolution Methods ===
2662
+ /**
2663
+ * Resolve an ENS name to an Ethereum address
2664
+ */
2665
+ async resolveENSName(ensName) {
2666
+ return this.ensResolver.resolveENSName(ensName);
2667
+ }
2668
+ /**
2669
+ * Resolve an address to its primary ENS name (reverse resolution)
2670
+ */
2671
+ async resolveAddressToENS(address) {
2672
+ return this.ensResolver.resolveAddressToENS(address);
2673
+ }
2674
+ /**
2675
+ * Get ENS avatar for a given ENS name
2676
+ */
2677
+ async getENSAvatar(ensName) {
2678
+ return this.ensResolver.getENSAvatar(ensName);
2679
+ }
2680
+ /**
2681
+ * Get ENS text record for a given ENS name and key
2682
+ */
2683
+ async getENSTextRecord(ensName, key) {
2684
+ return this.ensResolver.getENSTextRecord(ensName, key);
2685
+ }
2686
+ /**
2687
+ * Get complete ENS profile for a given ENS name
2688
+ */
2689
+ async getENSProfile(ensName) {
2690
+ return this.ensResolver.getENSProfile(ensName);
2691
+ }
2692
+ // === Basename Resolution Methods ===
2693
+ /**
2694
+ * Get basename from an Ethereum address
2695
+ */
2696
+ async getBasename(address) {
2697
+ return this.basenameResolver.getBasename(address);
2698
+ }
2699
+ /**
2700
+ * Get basename avatar for a given basename
2701
+ */
2702
+ async getBasenameAvatar(basename) {
2703
+ return this.basenameResolver.getBasenameAvatar(basename);
2704
+ }
2705
+ /**
2706
+ * Get basename text record for a given basename and key
2707
+ */
2708
+ async getBasenameTextRecord(basename, key) {
2709
+ return this.basenameResolver.getBasenameTextRecord(basename, key);
2710
+ }
2711
+ /**
2712
+ * Resolve basename to an Ethereum address
2713
+ */
2714
+ async getBasenameAddress(basename) {
2715
+ return this.basenameResolver.getBasenameAddress(basename);
2716
+ }
2717
+ /**
2718
+ * Get basename metadata for a given basename
2719
+ */
2720
+ async getBasenameMetadata(basename) {
2721
+ return this.basenameResolver.getBasenameMetadata(basename);
2722
+ }
2723
+ /**
2724
+ * Get complete basename profile for a given address
2725
+ */
2726
+ async resolveBasenameProfile(address) {
2727
+ return this.basenameResolver.resolveBasenameProfile(address);
2728
+ }
2729
+ // === XMTP Message Methods ===
2730
+ /**
2731
+ * Find any message by ID with caching
2732
+ */
2733
+ async findMessage(messageId) {
2734
+ return this.xmtpResolver.findMessage(messageId);
2735
+ }
2736
+ /**
2737
+ * Find root message by ID (traverses reply chain)
2738
+ */
2739
+ async findRootMessage(messageId) {
2740
+ return this.xmtpResolver.findRootMessage(messageId);
2741
+ }
2742
+ // === Universal Resolution Methods ===
2743
+ /**
2744
+ * Universal name resolution - tries to resolve any name (ENS or basename) to an address
2745
+ */
2746
+ async resolveName(name) {
2747
+ if (name.endsWith(".eth")) {
2748
+ return this.resolveENSName(name);
2749
+ }
2750
+ if (name.endsWith(".base.eth")) {
2751
+ return this.getBasenameAddress(name);
2752
+ }
2753
+ const ensResult = await this.resolveENSName(name);
2754
+ if (ensResult) {
2755
+ return ensResult;
2756
+ }
2757
+ return this.getBasenameAddress(name);
2758
+ }
2759
+ /**
2760
+ * Universal reverse resolution - tries to resolve an address to any name (ENS or basename)
2761
+ */
2762
+ async resolveAddressToName(address) {
2763
+ const basename = await this.getBasename(address);
2764
+ if (basename) {
2765
+ return basename;
2766
+ }
2767
+ return this.resolveAddressToENS(address);
2768
+ }
2769
+ /**
2770
+ * Get complete profile for an address (combines ENS and basename data)
2771
+ */
2772
+ async getCompleteProfile(address) {
2773
+ const [ensName, basename, ensProfile, basenameProfile] = await Promise.allSettled([
2774
+ this.resolveAddressToENS(address),
2775
+ this.getBasename(address),
2776
+ this.resolveAddressToENS(address).then(
2777
+ (name) => name ? this.getENSProfile(name) : null
2778
+ ),
2779
+ this.resolveBasenameProfile(address)
2780
+ ]);
2781
+ return {
2782
+ address,
2783
+ ensName: ensName.status === "fulfilled" ? ensName.value : null,
2784
+ basename: basename.status === "fulfilled" ? basename.value : null,
2785
+ ensProfile: ensProfile.status === "fulfilled" ? ensProfile.value : null,
2786
+ basenameProfile: basenameProfile.status === "fulfilled" ? basenameProfile.value : null
2787
+ };
2788
+ }
2789
+ // === Cache Management Methods ===
2790
+ /**
2791
+ * Pre-populate all resolver caches
2792
+ */
2793
+ async prePopulateAllCaches() {
2794
+ await Promise.allSettled([
2795
+ this.addressResolver.prePopulateCache(),
2796
+ this.xmtpResolver.prePopulateCache()
2797
+ ]);
2798
+ }
2799
+ /**
2800
+ * Create a complete XmtpSender object from an address or inboxId
2801
+ * Uses the resolver to get the best available name and profile information
2802
+ */
2803
+ async createXmtpSender(addressOrInboxId, conversationId) {
2804
+ let address = null;
2805
+ let inboxId = addressOrInboxId;
2806
+ if (addressOrInboxId.startsWith("0x") && addressOrInboxId.length === 42) {
2807
+ address = addressOrInboxId;
2808
+ inboxId = addressOrInboxId;
2809
+ } else {
2810
+ address = await this.resolveAddress(addressOrInboxId, conversationId);
2811
+ }
2812
+ let name = "Unknown";
2813
+ let basename;
2814
+ if (address) {
2815
+ const basenameResult = await this.getBasename(address);
2816
+ console.log(
2817
+ `\u{1F50D} [RESOLVER] Direct basename lookup for ${address}:`,
2818
+ basenameResult
2819
+ );
2820
+ const resolvedName = await this.resolveAddressToName(address);
2821
+ console.log(
2822
+ `\u{1F50D} [RESOLVER] Universal name resolution for ${address}:`,
2823
+ resolvedName
2824
+ );
2825
+ if (resolvedName) {
2826
+ name = resolvedName;
2827
+ if (resolvedName.endsWith(".base.eth")) {
2828
+ basename = resolvedName;
2829
+ }
2830
+ } else {
2831
+ name = `${address.slice(0, 6)}...${address.slice(-4)}`;
2832
+ }
2833
+ if (!basename) {
2834
+ const resolvedBasename = await this.getBasename(address);
2835
+ basename = resolvedBasename || void 0;
2836
+ }
2837
+ } else {
2838
+ name = `${inboxId.slice(0, 8)}...${inboxId.slice(-4)}`;
2839
+ }
2840
+ return {
2841
+ address: address || addressOrInboxId,
2842
+ inboxId,
2843
+ name,
2844
+ basename
2845
+ };
2846
+ }
2847
+ };
2848
+
2849
+ // src/lib/message-listener.ts
2850
+ var MessageListener = class extends EventEmitter {
2851
+ constructor(config) {
2852
+ super();
2853
+ __publicField(this, "xmtpClient");
2854
+ __publicField(this, "resolver");
2855
+ __publicField(this, "filter");
2856
+ __publicField(this, "heartbeatInterval");
2857
+ __publicField(this, "fallbackCheckInterval");
2858
+ __publicField(this, "messageCount", 0);
2859
+ __publicField(this, "conversations", []);
2860
+ __publicField(this, "config");
2861
+ this.xmtpClient = config.xmtpClient;
2862
+ const mainnetClient = createPublicClient({
2863
+ chain: mainnet2,
2864
+ transport: http2()
2865
+ });
2866
+ this.resolver = new Resolver({
2867
+ xmtpClient: this.xmtpClient,
2868
+ mainnetClient,
2869
+ baseClient: config.publicClient,
2870
+ maxCacheSize: 1e3,
2871
+ cacheTtl: 864e5
2872
+ // 24 hours
2873
+ });
2874
+ this.filter = config.filter;
2875
+ this.config = {
2876
+ heartbeatInterval: config.heartbeatInterval ?? 3e5,
2877
+ // 5 minutes
2878
+ conversationCheckInterval: config.conversationCheckInterval ?? 3e4,
2879
+ // 30 seconds
2880
+ envKey: config.envKey ?? "XMTP_ENV"
2881
+ };
2882
+ }
2883
+ // Type-safe event emitter methods
2884
+ on(event, listener) {
2885
+ return super.on(event, listener);
2886
+ }
2887
+ emit(event, ...args) {
2888
+ return super.emit(event, ...args);
2889
+ }
2890
+ async start() {
2891
+ const XMTP_ENV = process.env[this.config.envKey];
2892
+ await this.resolver?.prePopulateAllCaches();
2893
+ console.log("\u{1F4E1} Syncing conversations...");
2894
+ await this.xmtpClient.conversations.sync();
2895
+ const address = this.xmtpClient.accountIdentifier?.identifier;
2896
+ this.conversations = await this.xmtpClient.conversations.list();
2897
+ console.log(`\u{1F916} XMTP[${XMTP_ENV}] Listening on ${address} ...`);
2898
+ this.emit("started");
2899
+ try {
2900
+ const stream = await this.xmtpClient.conversations.streamAllMessages();
2901
+ this.heartbeatInterval = setInterval(() => {
2902
+ this.emit("heartbeat", {
2903
+ messageCount: this.messageCount,
2904
+ conversationCount: this.conversations.length
2905
+ });
2906
+ if (this.messageCount > 0) {
2907
+ console.log(`\u{1F493} Active - processed ${this.messageCount} messages`);
2908
+ }
2909
+ }, this.config.heartbeatInterval);
2910
+ this.fallbackCheckInterval = setInterval(async () => {
2911
+ try {
2912
+ const latestConversations = await this.xmtpClient.conversations.list();
2913
+ if (latestConversations.length > this.conversations.length) {
2914
+ console.log(
2915
+ `\u{1F195} Detected ${latestConversations.length - this.conversations.length} new conversations`
2916
+ );
2917
+ this.conversations.push(
2918
+ ...latestConversations.slice(this.conversations.length)
2919
+ );
2920
+ }
2921
+ } catch (error) {
2922
+ console.error("\u274C Error checking for new conversations:", error);
2923
+ this.emit("error", error);
2924
+ }
2925
+ }, this.config.conversationCheckInterval);
2926
+ try {
2927
+ for await (const message of stream) {
2928
+ this.messageCount++;
2929
+ try {
2930
+ if (!message || message.senderInboxId.toLowerCase() === this.xmtpClient.inboxId.toLowerCase()) {
2931
+ continue;
2932
+ }
2933
+ console.log(
2934
+ `\u{1F4E8} Received message "${JSON.stringify(message)}" in ${message.conversationId}`
2935
+ );
2936
+ const conversation = await this.xmtpClient.conversations.getConversationById(
2937
+ message.conversationId
2938
+ );
2939
+ if (!conversation) {
2940
+ console.log("\u274C Could not find conversation for message");
2941
+ continue;
2942
+ }
2943
+ const contentTypeId = message.contentType?.typeId;
2944
+ let messageContent;
2945
+ if (contentTypeId === "reply") {
2946
+ const replyContent = message.content;
2947
+ messageContent = (replyContent?.content || "").toString();
2948
+ } else if (contentTypeId === "remoteStaticAttachment" || contentTypeId === "attachment") {
2949
+ messageContent = message.fallback || message.content?.filename || "[Attachment]";
2950
+ } else if (contentTypeId === "reaction") {
2951
+ const reactionContent = message.content;
2952
+ messageContent = `[Reaction: ${reactionContent.content || ""}]`;
2953
+ } else {
2954
+ messageContent = message.content ? String(message.content) : "";
2955
+ }
2956
+ let rootMessage = message;
2957
+ let parentMessage = null;
2958
+ if (contentTypeId === "reply") {
2959
+ const { reference } = message.content;
2960
+ rootMessage = await this.resolver.findRootMessage(reference);
2961
+ parentMessage = await this.resolver.findMessage(reference);
2962
+ } else if (contentTypeId === "reaction") {
2963
+ const { reference } = message.content;
2964
+ rootMessage = await this.resolver.findRootMessage(reference);
2965
+ parentMessage = await this.resolver.findMessage(reference);
2966
+ } else {
2967
+ rootMessage = message;
2968
+ parentMessage = null;
2969
+ }
2970
+ if (!rootMessage) {
2971
+ console.warn(
2972
+ `\u26A0\uFE0F [MessageListener] Could not find root message for: ${message.id}`
2973
+ );
2974
+ continue;
2975
+ }
2976
+ if (this.filter) {
2977
+ const shouldProcess = await this.filter({
2978
+ conversation,
2979
+ message,
2980
+ rootMessage
2981
+ });
2982
+ if (!shouldProcess) {
2983
+ console.log("\u{1F504} Skipping message:", message.id);
2984
+ continue;
2985
+ }
2986
+ }
2987
+ const sender = await this.resolver.createXmtpSender(
2988
+ message.senderInboxId,
2989
+ message.conversationId
2990
+ );
2991
+ const subjects = {};
2992
+ const messageEvent = {
2993
+ conversation,
2994
+ message,
2995
+ rootMessage,
2996
+ // We already checked it's not null above
2997
+ parentMessage: parentMessage || void 0,
2998
+ sender,
2999
+ subjects
3000
+ };
3001
+ this.emit("message", messageEvent);
3002
+ } catch (messageError) {
3003
+ console.error("\u274C Error processing message:", messageError);
3004
+ this.emit("error", messageError);
3005
+ }
3006
+ }
3007
+ } catch (streamError) {
3008
+ console.error("\u274C Error in message stream:", streamError);
3009
+ this.cleanup();
3010
+ this.emit("error", streamError);
3011
+ console.log("\u{1F504} Attempting to restart stream...");
3012
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
3013
+ return this.start();
3014
+ }
3015
+ } catch (streamSetupError) {
3016
+ console.error("\u274C Error setting up message stream:", streamSetupError);
3017
+ this.emit("error", streamSetupError);
3018
+ throw streamSetupError;
3019
+ }
3020
+ }
3021
+ cleanup() {
3022
+ if (this.heartbeatInterval) {
3023
+ clearInterval(this.heartbeatInterval);
3024
+ }
3025
+ if (this.fallbackCheckInterval) {
3026
+ clearInterval(this.fallbackCheckInterval);
3027
+ }
3028
+ }
3029
+ stop() {
3030
+ this.cleanup();
3031
+ this.emit("stopped");
3032
+ console.log("\u{1F6D1} Message listener stopped");
3033
+ this.removeAllListeners();
3034
+ }
3035
+ /**
3036
+ * Get current statistics
3037
+ */
3038
+ getStats() {
3039
+ return {
3040
+ messageCount: this.messageCount,
3041
+ conversationCount: this.conversations.length,
3042
+ isActive: !!this.heartbeatInterval
3043
+ };
3044
+ }
3045
+ };
3046
+ async function startMessageListener(config) {
3047
+ const listener = new MessageListener(config);
3048
+ await listener.start();
3049
+ return listener;
3050
+ }
3051
+ function createMessageListener(config) {
3052
+ return new MessageListener(config);
3053
+ }
3054
+
3055
+ // src/lib/subjects.ts
3056
+ function extractMentionedNames(content) {
3057
+ const nameRegex = /@([a-zA-Z0-9-_]+\.(?:base\.)?eth)\b/gi;
3058
+ const matches = content.match(nameRegex);
3059
+ if (!matches) {
3060
+ return [];
3061
+ }
3062
+ const names = matches.map((match) => match.slice(1).toLowerCase());
3063
+ return [...new Set(names)];
3064
+ }
3065
+ async function resolveSubjects(mentionedNames, basenameResolver, ensResolver) {
3066
+ const subjects = {};
3067
+ if (mentionedNames.length === 0) {
3068
+ return subjects;
3069
+ }
3070
+ console.log(
3071
+ `\u{1F50D} Found ${mentionedNames.length} name mentions:`,
3072
+ mentionedNames
3073
+ );
3074
+ for (const mentionedName of mentionedNames) {
3075
+ try {
3076
+ let resolvedAddress = null;
3077
+ if (ensResolver.isENSName(mentionedName)) {
3078
+ console.log(`\u{1F50D} Resolving ENS name: ${mentionedName}`);
3079
+ resolvedAddress = await ensResolver.resolveENSName(mentionedName);
3080
+ } else {
3081
+ console.log(`\u{1F50D} Resolving basename: ${mentionedName}`);
3082
+ resolvedAddress = await basenameResolver.getBasenameAddress(mentionedName);
3083
+ }
3084
+ if (resolvedAddress) {
3085
+ subjects[mentionedName] = resolvedAddress;
3086
+ console.log(`\u2705 Resolved ${mentionedName} \u2192 ${resolvedAddress}`);
3087
+ } else {
3088
+ console.log(`\u274C Could not resolve address for: ${mentionedName}`);
3089
+ }
3090
+ } catch (error) {
3091
+ console.error(`\u274C Error resolving ${mentionedName}:`, error);
3092
+ }
3093
+ }
3094
+ return subjects;
3095
+ }
3096
+ async function extractSubjects(content, basenameResolver, ensResolver) {
3097
+ const mentionedNames = extractMentionedNames(content);
3098
+ return await resolveSubjects(mentionedNames, basenameResolver, ensResolver);
3099
+ }
3100
+
3101
+ // src/service-client.ts
3102
+ var XmtpServiceClient = class {
3103
+ constructor(config) {
3104
+ __publicField(this, "config");
3105
+ this.config = config;
3106
+ }
3107
+ async request(endpoint, body, method = "POST") {
3108
+ try {
3109
+ const baseUrl = this.config.serviceUrl.replace(/\/+$/, "");
3110
+ const isXmtpToolsEndpoint = endpoint.startsWith("/xmtp-tools/");
3111
+ const url = `${baseUrl}${endpoint}?token=${this.config.serviceToken}`;
3112
+ const headers = {
3113
+ "Content-Type": "application/json"
3114
+ };
3115
+ if (isXmtpToolsEndpoint) {
3116
+ headers.Authorization = `Bearer ${this.config.serviceToken}`;
3117
+ }
3118
+ const fetchOptions = {
3119
+ method,
3120
+ headers
3121
+ };
3122
+ if (method === "POST" && body) {
3123
+ fetchOptions.body = JSON.stringify(body);
3124
+ }
3125
+ const response = await fetch(url, fetchOptions);
3126
+ if (!response.ok) {
3127
+ let errorMessage = `HTTP ${response.status}`;
3128
+ try {
3129
+ const responseText = await response.text();
3130
+ try {
3131
+ const errorData = JSON.parse(responseText);
3132
+ errorMessage = errorData.error || errorMessage;
3133
+ } catch {
3134
+ errorMessage = responseText || errorMessage;
3135
+ }
3136
+ } catch {
3137
+ }
3138
+ throw new Error(errorMessage);
3139
+ }
3140
+ return {
3141
+ success: true,
3142
+ data: await response.json()
3143
+ };
3144
+ } catch (error) {
3145
+ console.error(
3146
+ `\u274C [XmtpServiceClient] Request to ${endpoint} failed:`,
3147
+ error
3148
+ );
3149
+ return {
3150
+ success: false,
3151
+ error: error instanceof Error ? error.message : "Unknown error"
3152
+ };
3153
+ }
3154
+ }
3155
+ async sendMessage(params) {
3156
+ return this.request("/xmtp-tools/send", {
3157
+ content: params.content
3158
+ });
3159
+ }
3160
+ async sendReply(params) {
3161
+ return this.request("/xmtp-tools/reply", {
3162
+ content: params.content,
3163
+ messageId: params.messageId
3164
+ });
3165
+ }
3166
+ async sendReaction(params) {
3167
+ return this.request("/xmtp-tools/react", {
3168
+ messageId: params.messageId,
3169
+ emoji: params.emoji,
3170
+ action: params.action
3171
+ });
3172
+ }
3173
+ async sendTransaction(params) {
3174
+ return this.request("/xmtp-tools/transaction", {
3175
+ fromAddress: params.fromAddress,
3176
+ chainId: params.chainId,
3177
+ calls: params.calls.map((call) => ({
3178
+ to: call.to,
3179
+ data: call.data,
3180
+ ...call.gas && { gas: call.gas },
3181
+ value: call.value || "0x0",
3182
+ metadata: {
3183
+ ...call.metadata,
3184
+ chainId: params.chainId,
3185
+ from: params.fromAddress,
3186
+ version: "1"
3187
+ }
3188
+ }))
3189
+ });
3190
+ }
3191
+ /**
3192
+ * Get a single message by ID
3193
+ */
3194
+ async getMessage(params) {
3195
+ return this.request(
3196
+ `/xmtp-tools/messages/${params.messageId}`,
3197
+ void 0,
3198
+ "GET"
3199
+ );
3200
+ }
3201
+ // getConversationMessages removed - superseded by thread-based approach
3202
+ };
3203
+ function createXmtpServiceClient(serviceUrl, serviceToken) {
3204
+ if (!serviceUrl || !serviceToken) {
3205
+ throw new Error("Missing XMTP service URL or token from runtime context");
3206
+ }
3207
+ return new XmtpServiceClient({
3208
+ serviceUrl,
3209
+ serviceToken
3210
+ });
3211
+ }
3212
+ function getXmtpAuthConfig(callbackUrl, callbackToken) {
3213
+ if (callbackUrl && callbackToken) {
3214
+ console.log("\u{1F511} [XmtpAuth] Using callback-provided credentials");
3215
+ return {
3216
+ serviceUrl: callbackUrl,
3217
+ serviceToken: callbackToken,
3218
+ source: "callback"
3219
+ };
3220
+ }
3221
+ const envUrl = process.env.XMTP_HOST;
3222
+ const envToken = process.env.XMTP_API_KEY;
3223
+ if (envUrl && envToken) {
3224
+ console.log("\u{1F511} [XmtpAuth] Using environment credentials");
3225
+ return {
3226
+ serviceUrl: envUrl,
3227
+ serviceToken: envToken,
3228
+ source: "environment"
3229
+ };
3230
+ }
3231
+ console.error(
3232
+ "\u274C [XmtpAuth] No XMTP credentials found in callback or environment"
3233
+ );
3234
+ console.error(
3235
+ "\u{1F4A1} [XmtpAuth] Expected: XMTP_HOST + XMTP_API_KEY or callback credentials"
3236
+ );
3237
+ return null;
3238
+ }
3239
+ function createAuthenticatedXmtpClient(callbackUrl, callbackToken) {
3240
+ const authConfig = getXmtpAuthConfig(callbackUrl, callbackToken);
3241
+ if (!authConfig) {
3242
+ throw new Error("No XMTP credentials found");
3243
+ }
3244
+ console.log(
3245
+ `\u{1F517} [XmtpAuth] Creating XMTP client (${authConfig.source} credentials)`
3246
+ );
3247
+ return createXmtpServiceClient(authConfig.serviceUrl, authConfig.serviceToken);
3248
+ }
3249
+ function getXMTPToolsUrl(baseUrl, action, token) {
3250
+ return `${baseUrl}/xmtp-tools/${action}?token=${token}`;
3251
+ }
3252
+
3253
+ // src/index.ts
3254
+ import {
3255
+ Client,
3256
+ IdentifierKind as IdentifierKind2
3257
+ } from "@xmtp/node-sdk";
3258
+ import {
3259
+ ContentTypeTransactionReference
3260
+ } from "@xmtp/content-type-transaction-reference";
3261
+ import { ContentTypeText } from "@xmtp/content-type-text";
3262
+ import {
3263
+ ContentTypeReaction
3264
+ } from "@xmtp/content-type-reaction";
3265
+ import {
3266
+ ContentTypeReply,
3267
+ ReplyCodec as ReplyCodec2
3268
+ } from "@xmtp/content-type-reply";
3269
+ import {
3270
+ ContentTypeGroupUpdated,
3271
+ GroupUpdatedCodec
3272
+ } from "@xmtp/content-type-group-updated";
3273
+ import {
3274
+ ContentTypeWalletSendCalls
3275
+ } from "@xmtp/content-type-wallet-send-calls";
3276
+ export {
3277
+ BasenameResolver,
3278
+ BasenameTextRecordKeys,
3279
+ Client,
3280
+ ContentTypeGroupUpdated,
3281
+ ContentTypeReaction,
3282
+ ContentTypeReply,
3283
+ ContentTypeText,
3284
+ ContentTypeTransactionReference,
3285
+ ContentTypeWalletSendCalls,
3286
+ DEFAULT_AMOUNT,
3287
+ DEFAULT_OPTIONS,
3288
+ ENSResolver,
3289
+ GroupUpdatedCodec,
3290
+ IdentifierKind2 as IdentifierKind,
3291
+ MAX_USDC_AMOUNT,
3292
+ MessageListener,
3293
+ ReplyCodec2 as ReplyCodec,
3294
+ Resolver,
3295
+ XMTPConnectionManager,
3296
+ XmtpResolver,
3297
+ XmtpServiceClient,
3298
+ backupDbToPersistentStorage,
3299
+ convertChainIdToCoinType,
3300
+ convertReverseNodeToBytes,
3301
+ createAuthenticatedXmtpClient,
3302
+ createMessageListener,
3303
+ createSigner,
3304
+ createUser,
3305
+ createXMTPClient,
3306
+ createXMTPConnectionManager,
3307
+ createXmtpServiceClient,
3308
+ diagnoseXMTPIdentityIssue,
3309
+ extractMentionedNames,
3310
+ extractSubjects,
3311
+ generateEncryptionKeyHex,
3312
+ getDbPath,
3313
+ getEncryptionKeyFromHex,
3314
+ getXMTPToolsUrl,
3315
+ getXmtpAuthConfig,
3316
+ logAgentDetails,
3317
+ resolveSubjects,
3318
+ resolveUserAddress,
3319
+ startMessageListener,
3320
+ startPeriodicBackup,
3321
+ validateEnvironment
3322
+ };
3323
+ //# sourceMappingURL=index.js.map