@hybrd/xmtp 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -7
- package/dist/index.cjs +415 -3085
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -828
- package/dist/index.d.ts +12 -828
- package/dist/index.js +416 -3056
- package/dist/index.js.map +1 -1
- package/package.json +16 -3
- package/src/client.ts +23 -135
- package/src/index.ts +28 -81
- package/src/index.ts.old +145 -0
- package/src/lib/jwt.ts +45 -13
- package/src/lib/{message-listener.test.ts → message-listener.test.ts.old} +1 -1
- package/src/lib/subjects.ts +6 -5
- package/src/plugin.filters.test.ts +158 -0
- package/src/plugin.ts +456 -23
- package/src/resolver/address-resolver.ts +217 -211
- package/src/resolver/basename-resolver.ts +6 -5
- package/src/resolver/ens-resolver.ts +15 -14
- package/src/resolver/resolver.ts +3 -2
- package/src/resolver/xmtp-resolver.ts +10 -9
- package/src/{service-client.ts → service-client.ts.old} +26 -3
- package/src/types.ts +9 -157
- package/src/types.ts.old +157 -0
- package/.cache/tsbuildinfo.json +0 -1
- package/.turbo/turbo-build.log +0 -45
- package/.turbo/turbo-lint$colon$fix.log +0 -6
- package/.turbo/turbo-typecheck.log +0 -5
- package/biome.jsonc +0 -4
- package/scripts/generate-keys.ts +0 -25
- package/scripts/refresh-identity.ts +0 -119
- package/scripts/register-wallet.ts +0 -95
- package/scripts/revoke-all-installations.ts +0 -91
- package/scripts/revoke-installations.ts +0 -94
- package/src/endpoints.ts +0 -306
- package/tsconfig.json +0 -9
- package/tsup.config.ts +0 -14
- /package/src/lib/{message-listener.ts → message-listener.ts.old} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -32,62 +32,47 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
var src_exports = {};
|
|
34
34
|
__export(src_exports, {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Client: () => import_node_sdk2.Client,
|
|
35
|
+
Agent: () => import_agent_sdk2.Agent,
|
|
36
|
+
Client: () => import_node_sdk3.Client,
|
|
38
37
|
ContentTypeGroupUpdated: () => import_content_type_group_updated.ContentTypeGroupUpdated,
|
|
39
|
-
ContentTypeReaction: () =>
|
|
40
|
-
ContentTypeReply: () =>
|
|
41
|
-
ContentTypeText: () =>
|
|
38
|
+
ContentTypeReaction: () => import_content_type_reaction2.ContentTypeReaction,
|
|
39
|
+
ContentTypeReply: () => import_content_type_reply2.ContentTypeReply,
|
|
40
|
+
ContentTypeText: () => import_content_type_text.ContentTypeText,
|
|
42
41
|
ContentTypeTransactionReference: () => import_content_type_transaction_reference2.ContentTypeTransactionReference,
|
|
43
|
-
ContentTypeWalletSendCalls: () =>
|
|
42
|
+
ContentTypeWalletSendCalls: () => import_content_type_wallet_send_calls2.ContentTypeWalletSendCalls,
|
|
44
43
|
DEFAULT_AMOUNT: () => DEFAULT_AMOUNT,
|
|
45
44
|
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
|
|
46
|
-
ENSResolver: () => ENSResolver,
|
|
47
45
|
GroupUpdatedCodec: () => import_content_type_group_updated.GroupUpdatedCodec,
|
|
48
|
-
IdentifierKind: () =>
|
|
46
|
+
IdentifierKind: () => import_node_sdk3.IdentifierKind,
|
|
49
47
|
MAX_USDC_AMOUNT: () => MAX_USDC_AMOUNT,
|
|
50
|
-
|
|
51
|
-
ReplyCodec: () => import_content_type_reply3.ReplyCodec,
|
|
52
|
-
Resolver: () => Resolver,
|
|
48
|
+
ReplyCodec: () => import_content_type_reply2.ReplyCodec,
|
|
53
49
|
XMTPConnectionManager: () => XMTPConnectionManager,
|
|
54
50
|
XMTPPlugin: () => XMTPPlugin,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
backupDbToPersistentStorage: () => backupDbToPersistentStorage,
|
|
58
|
-
convertChainIdToCoinType: () => convertChainIdToCoinType,
|
|
59
|
-
convertReverseNodeToBytes: () => convertReverseNodeToBytes,
|
|
60
|
-
createAuthenticatedXmtpClient: () => createAuthenticatedXmtpClient,
|
|
61
|
-
createMessageListener: () => createMessageListener,
|
|
62
|
-
createSigner: () => createSigner,
|
|
63
|
-
createUser: () => createUser,
|
|
51
|
+
createSigner: () => import_agent_sdk2.createSigner,
|
|
52
|
+
createUser: () => import_agent_sdk2.createUser,
|
|
64
53
|
createXMTPClient: () => createXMTPClient,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
diagnoseXMTPIdentityIssue: () => diagnoseXMTPIdentityIssue,
|
|
68
|
-
extractMentionedNames: () => extractMentionedNames,
|
|
69
|
-
extractSubjects: () => extractSubjects,
|
|
70
|
-
generateEncryptionKeyHex: () => generateEncryptionKeyHex,
|
|
54
|
+
createXMTPSigner: () => createSigner,
|
|
55
|
+
filter: () => import_agent_sdk2.filter,
|
|
71
56
|
generateXMTPToolsToken: () => generateXMTPToolsToken,
|
|
72
|
-
|
|
73
|
-
getEncryptionKeyFromHex: () => getEncryptionKeyFromHex,
|
|
74
|
-
getXMTPToolsUrl: () => getXMTPToolsUrl,
|
|
75
|
-
getXmtpAuthConfig: () => getXmtpAuthConfig,
|
|
57
|
+
getTestUrl: () => import_agent_sdk2.getTestUrl,
|
|
76
58
|
logAgentDetails: () => logAgentDetails,
|
|
77
|
-
resolveSubjects: () => resolveSubjects,
|
|
78
|
-
resolveUserAddress: () => resolveUserAddress,
|
|
79
|
-
startMessageListener: () => startMessageListener,
|
|
80
|
-
startPeriodicBackup: () => startPeriodicBackup,
|
|
81
59
|
validateEnvironment: () => validateEnvironment
|
|
82
60
|
});
|
|
83
61
|
module.exports = __toCommonJS(src_exports);
|
|
62
|
+
var import_agent_sdk2 = require("@xmtp/agent-sdk");
|
|
63
|
+
|
|
64
|
+
// src/constants.ts
|
|
65
|
+
var DEFAULT_OPTIONS = ["yes", "no"];
|
|
66
|
+
var DEFAULT_AMOUNT = "0.1";
|
|
67
|
+
var MAX_USDC_AMOUNT = 10;
|
|
84
68
|
|
|
85
69
|
// src/client.ts
|
|
70
|
+
var import_utils = require("@hybrd/utils");
|
|
86
71
|
var import_content_type_reaction = require("@xmtp/content-type-reaction");
|
|
87
72
|
var import_content_type_reply = require("@xmtp/content-type-reply");
|
|
88
73
|
var import_content_type_transaction_reference = require("@xmtp/content-type-transaction-reference");
|
|
89
74
|
var import_content_type_wallet_send_calls = require("@xmtp/content-type-wallet-send-calls");
|
|
90
|
-
var
|
|
75
|
+
var import_node_sdk2 = require("@xmtp/node-sdk");
|
|
91
76
|
var import_node_crypto = require("crypto");
|
|
92
77
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
93
78
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -98,6 +83,7 @@ var import_accounts = require("viem/accounts");
|
|
|
98
83
|
var import_chains = require("viem/chains");
|
|
99
84
|
|
|
100
85
|
// scripts/revoke-installations.ts
|
|
86
|
+
var import_node_sdk = require("@xmtp/node-sdk");
|
|
101
87
|
var import_meta = {};
|
|
102
88
|
async function revokeOldInstallations(signer, inboxId) {
|
|
103
89
|
console.log("\u{1F527} Attempting to revoke old installations...");
|
|
@@ -106,7 +92,7 @@ async function revokeOldInstallations(signer, inboxId) {
|
|
|
106
92
|
console.log("\u2139\uFE0F No inboxId provided, cannot revoke installations");
|
|
107
93
|
return false;
|
|
108
94
|
}
|
|
109
|
-
const inboxStates = await
|
|
95
|
+
const inboxStates = await import_node_sdk.Client.inboxStateFromInboxIds(
|
|
110
96
|
[inboxId],
|
|
111
97
|
process.env.XMTP_ENV
|
|
112
98
|
);
|
|
@@ -117,13 +103,13 @@ async function revokeOldInstallations(signer, inboxId) {
|
|
|
117
103
|
const toRevokeInstallationBytes = inboxStates[0].installations.map(
|
|
118
104
|
(i) => i.bytes
|
|
119
105
|
);
|
|
120
|
-
await
|
|
106
|
+
await import_node_sdk.Client.revokeInstallations(
|
|
121
107
|
signer,
|
|
122
108
|
inboxId,
|
|
123
109
|
toRevokeInstallationBytes,
|
|
124
110
|
process.env.XMTP_ENV
|
|
125
111
|
);
|
|
126
|
-
const resultingStates = await
|
|
112
|
+
const resultingStates = await import_node_sdk.Client.inboxStateFromInboxIds(
|
|
127
113
|
[inboxId],
|
|
128
114
|
process.env.XMTP_ENV
|
|
129
115
|
);
|
|
@@ -210,7 +196,7 @@ var createSigner = (key) => {
|
|
|
210
196
|
};
|
|
211
197
|
};
|
|
212
198
|
async function clearXMTPDatabase(address, env) {
|
|
213
|
-
|
|
199
|
+
import_utils.logger.debug("\u{1F9F9} Clearing XMTP database to resolve installation limit...");
|
|
214
200
|
const getStorageDirectory = () => {
|
|
215
201
|
const customStoragePath = process.env.XMTP_STORAGE_PATH;
|
|
216
202
|
if (customStoragePath) {
|
|
@@ -239,9 +225,9 @@ async function clearXMTPDatabase(address, env) {
|
|
|
239
225
|
const fullPath = import_node_path.default.join(dir, file);
|
|
240
226
|
try {
|
|
241
227
|
import_node_fs.default.unlinkSync(fullPath);
|
|
242
|
-
|
|
228
|
+
import_utils.logger.debug(`\u2705 Removed: ${fullPath}`);
|
|
243
229
|
} catch (err) {
|
|
244
|
-
|
|
230
|
+
import_utils.logger.debug(`\u26A0\uFE0F Could not remove ${fullPath}:`, err);
|
|
245
231
|
}
|
|
246
232
|
}
|
|
247
233
|
}
|
|
@@ -258,12 +244,12 @@ async function createXMTPClient(privateKey, opts) {
|
|
|
258
244
|
"No signer provided and XMTP_WALLET_KEY environment variable is not set"
|
|
259
245
|
);
|
|
260
246
|
}
|
|
261
|
-
const {
|
|
247
|
+
const { XMTP_DB_ENCRYPTION_KEY, XMTP_ENV } = process.env;
|
|
262
248
|
const identifier = await signer.getIdentifier();
|
|
263
249
|
const address = identifier.identifier;
|
|
264
250
|
while (attempt < maxRetries) {
|
|
265
251
|
try {
|
|
266
|
-
|
|
252
|
+
import_utils.logger.debug(
|
|
267
253
|
`\u{1F504} Attempt ${attempt + 1}/${maxRetries} to create XMTP client...`
|
|
268
254
|
);
|
|
269
255
|
if (!persist) {
|
|
@@ -271,16 +257,18 @@ async function createXMTPClient(privateKey, opts) {
|
|
|
271
257
|
"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."
|
|
272
258
|
);
|
|
273
259
|
}
|
|
274
|
-
if (!
|
|
275
|
-
throw new Error(
|
|
260
|
+
if (!XMTP_DB_ENCRYPTION_KEY) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
"XMTP_DB_ENCRYPTION_KEY must be set for persistent mode"
|
|
263
|
+
);
|
|
276
264
|
}
|
|
277
|
-
const dbEncryptionKey = getEncryptionKeyFromHex(
|
|
265
|
+
const dbEncryptionKey = getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY);
|
|
278
266
|
const dbPath = await getDbPath(
|
|
279
267
|
`${XMTP_ENV || "dev"}-${address}`,
|
|
280
268
|
storagePath
|
|
281
269
|
);
|
|
282
|
-
|
|
283
|
-
const client = await
|
|
270
|
+
import_utils.logger.debug(`\u{1F4C1} Using database path: ${dbPath}`);
|
|
271
|
+
const client = await import_node_sdk2.Client.create(signer, {
|
|
284
272
|
dbEncryptionKey,
|
|
285
273
|
env: XMTP_ENV,
|
|
286
274
|
dbPath,
|
|
@@ -291,16 +279,15 @@ async function createXMTPClient(privateKey, opts) {
|
|
|
291
279
|
new import_content_type_transaction_reference.TransactionReferenceCodec()
|
|
292
280
|
]
|
|
293
281
|
});
|
|
294
|
-
|
|
282
|
+
import_utils.logger.debug("\u{1F4E1} Syncing conversations to ensure latest state...");
|
|
295
283
|
await client.conversations.sync();
|
|
296
284
|
await backupDbToPersistentStorage(
|
|
297
285
|
dbPath,
|
|
298
286
|
`${XMTP_ENV || "dev"}-${address}`
|
|
299
287
|
);
|
|
300
|
-
console.log(
|
|
301
|
-
console.log(
|
|
302
|
-
console.log(
|
|
303
|
-
console.log(`\u{1F4BE} Storage mode: persistent`);
|
|
288
|
+
console.log(`Wallet: ${address}`);
|
|
289
|
+
console.log(`Env: ${XMTP_ENV || "dev"}`);
|
|
290
|
+
console.log(`Storage: persistent`);
|
|
304
291
|
return client;
|
|
305
292
|
} catch (error) {
|
|
306
293
|
attempt++;
|
|
@@ -347,8 +334,8 @@ async function createXMTPClient(privateKey, opts) {
|
|
|
347
334
|
console.log("\u{1F527} Attempting automatic identity refresh...");
|
|
348
335
|
try {
|
|
349
336
|
console.log("\u{1F4DD} Creating persistent client to refresh identity...");
|
|
350
|
-
const tempEncryptionKey =
|
|
351
|
-
const tempClient = await
|
|
337
|
+
const tempEncryptionKey = XMTP_DB_ENCRYPTION_KEY ? getEncryptionKeyFromHex(XMTP_DB_ENCRYPTION_KEY) : getEncryptionKeyFromHex(generateEncryptionKeyHex());
|
|
338
|
+
const tempClient = await import_node_sdk2.Client.create(signer, {
|
|
352
339
|
dbEncryptionKey: tempEncryptionKey,
|
|
353
340
|
env: XMTP_ENV,
|
|
354
341
|
dbPath: await getDbPath(
|
|
@@ -516,78 +503,6 @@ function validateEnvironment(vars) {
|
|
|
516
503
|
return acc;
|
|
517
504
|
}, {});
|
|
518
505
|
}
|
|
519
|
-
async function diagnoseXMTPIdentityIssue(client, inboxId, environment) {
|
|
520
|
-
const suggestions = [];
|
|
521
|
-
const details = {
|
|
522
|
-
environment,
|
|
523
|
-
inboxId,
|
|
524
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
525
|
-
};
|
|
526
|
-
try {
|
|
527
|
-
const inboxState = await client.preferences.inboxStateFromInboxIds([
|
|
528
|
-
inboxId
|
|
529
|
-
]);
|
|
530
|
-
if (inboxState.length === 0) {
|
|
531
|
-
suggestions.push(
|
|
532
|
-
`Inbox ID ${inboxId} not found in ${environment} environment`
|
|
533
|
-
);
|
|
534
|
-
suggestions.push(
|
|
535
|
-
"Try switching XMTP_ENV to 'dev' if currently 'production' or vice versa"
|
|
536
|
-
);
|
|
537
|
-
suggestions.push(
|
|
538
|
-
"Verify the user has created an identity on this XMTP network"
|
|
539
|
-
);
|
|
540
|
-
details.inboxStateFound = false;
|
|
541
|
-
return { canResolve: false, suggestions, details };
|
|
542
|
-
}
|
|
543
|
-
const inbox = inboxState[0];
|
|
544
|
-
if (!inbox) {
|
|
545
|
-
suggestions.push("Inbox state returned empty data");
|
|
546
|
-
details.inboxStateFound = false;
|
|
547
|
-
return { canResolve: false, suggestions, details };
|
|
548
|
-
}
|
|
549
|
-
details.inboxStateFound = true;
|
|
550
|
-
details.identifierCount = inbox.identifiers?.length || 0;
|
|
551
|
-
if (!inbox.identifiers || inbox.identifiers.length === 0) {
|
|
552
|
-
suggestions.push("Inbox found but has no identifiers");
|
|
553
|
-
suggestions.push("This indicates incomplete identity registration");
|
|
554
|
-
suggestions.push("User may need to re-register their identity on XMTP");
|
|
555
|
-
details.hasIdentifiers = false;
|
|
556
|
-
return { canResolve: false, suggestions, details };
|
|
557
|
-
}
|
|
558
|
-
details.hasIdentifiers = true;
|
|
559
|
-
details.resolvedAddress = inbox.identifiers[0]?.identifier;
|
|
560
|
-
return {
|
|
561
|
-
canResolve: true,
|
|
562
|
-
suggestions: ["Identity resolved successfully"],
|
|
563
|
-
details
|
|
564
|
-
};
|
|
565
|
-
} catch (error) {
|
|
566
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
567
|
-
details.error = errorMessage;
|
|
568
|
-
if (errorMessage.includes("Association error")) {
|
|
569
|
-
suggestions.push("XMTP identity association error detected");
|
|
570
|
-
suggestions.push(
|
|
571
|
-
"Check if user exists on the correct XMTP environment (dev vs production)"
|
|
572
|
-
);
|
|
573
|
-
suggestions.push(
|
|
574
|
-
"Identity may need to be recreated on the current environment"
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
if (errorMessage.includes("Missing identity update")) {
|
|
578
|
-
suggestions.push("Missing identity updates in XMTP network");
|
|
579
|
-
suggestions.push("This can indicate network sync issues");
|
|
580
|
-
suggestions.push("Wait a few minutes and retry, or recreate identity");
|
|
581
|
-
}
|
|
582
|
-
if (errorMessage.includes("database") || errorMessage.includes("storage")) {
|
|
583
|
-
suggestions.push("XMTP local database/storage issue");
|
|
584
|
-
suggestions.push("Try clearing XMTP database and resyncing");
|
|
585
|
-
suggestions.push("Check .data/xmtp directory permissions");
|
|
586
|
-
}
|
|
587
|
-
suggestions.push("Consider testing with a fresh XMTP identity");
|
|
588
|
-
return { canResolve: false, suggestions, details };
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
506
|
var XMTPConnectionManager = class {
|
|
592
507
|
constructor(privateKey, config = {}) {
|
|
593
508
|
__publicField(this, "client", null);
|
|
@@ -715,2966 +630,402 @@ var XMTPConnectionManager = class {
|
|
|
715
630
|
console.log("\u{1F50C} XMTP client disconnected");
|
|
716
631
|
}
|
|
717
632
|
};
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
async function
|
|
724
|
-
|
|
725
|
-
|
|
633
|
+
|
|
634
|
+
// src/plugin.ts
|
|
635
|
+
var import_agent_sdk = require("@xmtp/agent-sdk");
|
|
636
|
+
var import_utils2 = require("@hybrd/utils");
|
|
637
|
+
var import_node_crypto2 = require("crypto");
|
|
638
|
+
async function sendResponse(conversation, text, originalMessageId, behaviorContext) {
|
|
639
|
+
const shouldThread = behaviorContext?.sendOptions?.threaded ?? false;
|
|
640
|
+
if (shouldThread) {
|
|
726
641
|
try {
|
|
727
|
-
|
|
728
|
-
|
|
642
|
+
const reply = {
|
|
643
|
+
reference: originalMessageId,
|
|
644
|
+
contentType: import_content_type_text.ContentTypeText,
|
|
645
|
+
content: text
|
|
646
|
+
};
|
|
647
|
+
await conversation.send(reply, import_content_type_reply2.ContentTypeReply);
|
|
648
|
+
import_utils2.logger.debug(
|
|
649
|
+
`\u2705 [sendResponse] Threaded reply sent successfully to message ${originalMessageId}`
|
|
729
650
|
);
|
|
730
|
-
const inboxState = await client.preferences.inboxStateFromInboxIds([
|
|
731
|
-
senderInboxId
|
|
732
|
-
]);
|
|
733
|
-
const firstInbox = inboxState[0];
|
|
734
|
-
if (inboxState.length > 0 && firstInbox?.identifiers && firstInbox.identifiers.length > 0) {
|
|
735
|
-
const userAddress = firstInbox.identifiers[0]?.identifier;
|
|
736
|
-
if (userAddress) {
|
|
737
|
-
console.log("\u2705 Resolved user address:", userAddress);
|
|
738
|
-
return userAddress;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
console.log("\u26A0\uFE0F No identifiers found in inbox state");
|
|
742
|
-
return "unknown";
|
|
743
651
|
} catch (error) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
console.log(
|
|
751
|
-
"\u{1F527} Attempting automatic identity refresh for address resolution..."
|
|
752
|
-
);
|
|
753
|
-
try {
|
|
754
|
-
console.log("\u{1F4E1} Syncing conversations to refresh identity...");
|
|
755
|
-
await client.conversations.sync();
|
|
756
|
-
console.log("\u23F3 Waiting 2s before retry...");
|
|
757
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
758
|
-
console.log(
|
|
759
|
-
"\u2705 Identity sync completed, retrying address resolution..."
|
|
760
|
-
);
|
|
761
|
-
} catch (refreshError) {
|
|
762
|
-
console.log(`\u274C Identity refresh failed:`, refreshError);
|
|
763
|
-
}
|
|
764
|
-
} else {
|
|
765
|
-
console.error("\u274C Failed to resolve user address after all retries");
|
|
766
|
-
console.error("\u{1F4A1} Identity association issue persists");
|
|
767
|
-
try {
|
|
768
|
-
const diagnosis = await diagnoseXMTPIdentityIssue(
|
|
769
|
-
client,
|
|
770
|
-
senderInboxId,
|
|
771
|
-
process.env.XMTP_ENV || "dev"
|
|
772
|
-
);
|
|
773
|
-
console.log("\u{1F50D} XMTP Identity Diagnosis:");
|
|
774
|
-
diagnosis.suggestions.forEach((suggestion) => {
|
|
775
|
-
console.error(`\u{1F4A1} ${suggestion}`);
|
|
776
|
-
});
|
|
777
|
-
} catch (diagError) {
|
|
778
|
-
console.warn("\u26A0\uFE0F Could not run XMTP identity diagnosis:", diagError);
|
|
779
|
-
}
|
|
780
|
-
return "unknown";
|
|
781
|
-
}
|
|
782
|
-
} else {
|
|
783
|
-
console.error("\u274C Error resolving user address:", error);
|
|
784
|
-
return "unknown";
|
|
785
|
-
}
|
|
652
|
+
import_utils2.logger.error(
|
|
653
|
+
`\u274C [sendResponse] Failed to send threaded reply to message ${originalMessageId}:`,
|
|
654
|
+
error
|
|
655
|
+
);
|
|
656
|
+
import_utils2.logger.debug(`\u{1F504} [sendResponse] Falling back to regular message`);
|
|
657
|
+
await conversation.send(text);
|
|
786
658
|
}
|
|
659
|
+
} else {
|
|
660
|
+
await conversation.send(text);
|
|
787
661
|
}
|
|
788
|
-
return "unknown";
|
|
789
662
|
}
|
|
790
|
-
|
|
791
|
-
return
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
663
|
+
function XMTPPlugin() {
|
|
664
|
+
return {
|
|
665
|
+
name: "xmtp",
|
|
666
|
+
description: "Provides XMTP messaging functionality",
|
|
667
|
+
apply: async (app, context) => {
|
|
668
|
+
const {
|
|
669
|
+
XMTP_WALLET_KEY,
|
|
670
|
+
XMTP_DB_ENCRYPTION_KEY,
|
|
671
|
+
XMTP_ENV = "production"
|
|
672
|
+
} = process.env;
|
|
673
|
+
const { agent } = context;
|
|
674
|
+
const pluginContext = context;
|
|
675
|
+
if (!XMTP_WALLET_KEY) {
|
|
676
|
+
throw new Error("XMTP_WALLET_KEY must be set");
|
|
677
|
+
}
|
|
678
|
+
if (!XMTP_DB_ENCRYPTION_KEY) {
|
|
679
|
+
throw new Error("XMTP_DB_ENCRYPTION_KEY must be set");
|
|
680
|
+
}
|
|
681
|
+
const user = (0, import_agent_sdk.createUser)(XMTP_WALLET_KEY);
|
|
682
|
+
const signer = (0, import_agent_sdk.createSigner)(user);
|
|
683
|
+
const xmtpClient = await createXMTPClient(
|
|
684
|
+
XMTP_WALLET_KEY
|
|
685
|
+
);
|
|
686
|
+
async function startNodeStream() {
|
|
687
|
+
try {
|
|
688
|
+
import_utils2.logger.debug("\u{1F3A7} XMTP node client stream initializing");
|
|
689
|
+
const stream = await xmtpClient.conversations.streamAllMessages();
|
|
690
|
+
import_utils2.logger.debug("\u{1F3A7} XMTP node client stream started");
|
|
691
|
+
for await (const msg of stream) {
|
|
692
|
+
try {
|
|
693
|
+
if (msg.senderInboxId === xmtpClient.inboxId) continue;
|
|
694
|
+
const content = typeof msg.content === "string" ? msg.content : (() => {
|
|
695
|
+
try {
|
|
696
|
+
return JSON.stringify(msg.content);
|
|
697
|
+
} catch {
|
|
698
|
+
return String(msg.content);
|
|
699
|
+
}
|
|
700
|
+
})();
|
|
701
|
+
const conversation = await xmtpClient.conversations.getConversationById(
|
|
702
|
+
msg.conversationId
|
|
703
|
+
);
|
|
704
|
+
if (!conversation) {
|
|
705
|
+
import_utils2.logger.warn(
|
|
706
|
+
`\u26A0\uFE0F XMTP conversation not found: ${msg.conversationId}`
|
|
707
|
+
);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const messages = [
|
|
711
|
+
{
|
|
712
|
+
id: (0, import_node_crypto2.randomUUID)(),
|
|
713
|
+
role: "user",
|
|
714
|
+
parts: [{ type: "text", text: content }]
|
|
715
|
+
}
|
|
716
|
+
];
|
|
717
|
+
const baseRuntime = {
|
|
718
|
+
conversation,
|
|
719
|
+
message: msg,
|
|
720
|
+
xmtpClient
|
|
721
|
+
};
|
|
722
|
+
const runtime = await agent.createRuntimeContext(baseRuntime);
|
|
723
|
+
if (pluginContext.behaviors) {
|
|
724
|
+
const behaviorContext2 = {
|
|
725
|
+
runtime,
|
|
726
|
+
client: xmtpClient,
|
|
727
|
+
conversation,
|
|
728
|
+
message: msg
|
|
729
|
+
};
|
|
730
|
+
await pluginContext.behaviors.executeBefore(behaviorContext2);
|
|
731
|
+
if (behaviorContext2.sendOptions?.filtered) {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const { text } = await agent.generate(messages, { runtime });
|
|
736
|
+
const behaviorContext = {
|
|
737
|
+
runtime,
|
|
738
|
+
client: xmtpClient,
|
|
739
|
+
conversation,
|
|
740
|
+
message: msg,
|
|
741
|
+
response: text
|
|
742
|
+
};
|
|
743
|
+
if (pluginContext.behaviors) {
|
|
744
|
+
await pluginContext.behaviors.executeAfter(behaviorContext);
|
|
745
|
+
}
|
|
746
|
+
if (behaviorContext?.sendOptions?.filtered) {
|
|
747
|
+
import_utils2.logger.debug(
|
|
748
|
+
`\u{1F507} [XMTP Plugin] Skipping response due to message being filtered`
|
|
749
|
+
);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
await sendResponse(conversation, text, msg.id, behaviorContext);
|
|
753
|
+
} catch (err) {
|
|
754
|
+
import_utils2.logger.error("\u274C Error processing XMTP message:", err);
|
|
755
|
+
}
|
|
842
756
|
}
|
|
757
|
+
} catch (err) {
|
|
758
|
+
import_utils2.logger.error("\u274C XMTP node client stream failed:", err);
|
|
843
759
|
}
|
|
844
760
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
console.log(`\u26A0\uFE0F No identifiers found for inbox ${inboxId}`);
|
|
852
|
-
return null;
|
|
853
|
-
} catch (error) {
|
|
854
|
-
console.error(`\u274C Error resolving user address for ${inboxId}:`, error);
|
|
855
|
-
return null;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* Resolve address from conversation members
|
|
860
|
-
*/
|
|
861
|
-
async resolveFromConversation(conversation, inboxId) {
|
|
862
|
-
try {
|
|
863
|
-
const members = await conversation.members();
|
|
864
|
-
const sender = members.find(
|
|
865
|
-
(member) => member.inboxId.toLowerCase() === inboxId.toLowerCase()
|
|
761
|
+
const enabledFromEnv = process.env.XMTP_ENABLE_NODE_STREAM;
|
|
762
|
+
const isNodeStreamEnabled = enabledFromEnv === void 0 ? true : !/^(false|0|off|no)$/i.test(String(enabledFromEnv));
|
|
763
|
+
if (isNodeStreamEnabled) void startNodeStream();
|
|
764
|
+
const address = user.account.address.toLowerCase();
|
|
765
|
+
const agentDbPath = await getDbPath(
|
|
766
|
+
`agent-${XMTP_ENV || "dev"}-${address}`
|
|
866
767
|
);
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
return ethIdentifier.identifier;
|
|
874
|
-
} else {
|
|
875
|
-
console.log(`\u26A0\uFE0F No Ethereum identifier found for inbox ${inboxId}`);
|
|
876
|
-
}
|
|
877
|
-
} else {
|
|
878
|
-
console.log(
|
|
879
|
-
`\u26A0\uFE0F Sender not found in conversation members for inbox ${inboxId}`
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
} catch (error) {
|
|
883
|
-
console.error(`\u274C Error resolving from conversation members:`, error);
|
|
884
|
-
}
|
|
885
|
-
return null;
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Resolve address from inbox state (network fallback)
|
|
889
|
-
*/
|
|
890
|
-
async resolveFromInboxState(inboxId) {
|
|
891
|
-
try {
|
|
892
|
-
const inboxState = await this.client.preferences.inboxStateFromInboxIds([
|
|
893
|
-
inboxId
|
|
894
|
-
]);
|
|
895
|
-
const firstState = inboxState?.[0];
|
|
896
|
-
if (firstState?.identifiers && firstState.identifiers.length > 0) {
|
|
897
|
-
const firstIdentifier = firstState.identifiers[0];
|
|
898
|
-
return firstIdentifier?.identifier;
|
|
899
|
-
}
|
|
900
|
-
} catch (error) {
|
|
901
|
-
console.error(`\u274C Error resolving from inbox state:`, error);
|
|
902
|
-
}
|
|
903
|
-
return null;
|
|
904
|
-
}
|
|
905
|
-
/**
|
|
906
|
-
* Get cached address if not expired
|
|
907
|
-
*/
|
|
908
|
-
getCachedAddress(inboxId) {
|
|
909
|
-
const entry = this.cache.get(inboxId);
|
|
910
|
-
if (!entry) return null;
|
|
911
|
-
const now = Date.now();
|
|
912
|
-
if (now - entry.timestamp > this.cacheTtl) {
|
|
913
|
-
this.cache.delete(inboxId);
|
|
914
|
-
return null;
|
|
915
|
-
}
|
|
916
|
-
return entry.address;
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Cache address with LRU eviction
|
|
920
|
-
*/
|
|
921
|
-
setCachedAddress(inboxId, address) {
|
|
922
|
-
if (this.cache.size >= this.maxCacheSize) {
|
|
923
|
-
const firstKey = this.cache.keys().next().value;
|
|
924
|
-
if (firstKey) {
|
|
925
|
-
this.cache.delete(firstKey);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
this.cache.set(inboxId, {
|
|
929
|
-
address,
|
|
930
|
-
timestamp: Date.now()
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Pre-populate cache from existing conversations
|
|
935
|
-
*/
|
|
936
|
-
async prePopulateCache() {
|
|
937
|
-
console.log("\u{1F504} Pre-populating address cache...");
|
|
938
|
-
try {
|
|
939
|
-
const conversations = await this.client.conversations.list();
|
|
940
|
-
let cachedCount = 0;
|
|
941
|
-
for (const conversation of conversations) {
|
|
768
|
+
import_utils2.logger.debug(`\u{1F4C1} Using agent listener database path: ${agentDbPath}`);
|
|
769
|
+
const xmtp = await import_agent_sdk.Agent.create(signer, {
|
|
770
|
+
env: XMTP_ENV,
|
|
771
|
+
dbPath: agentDbPath
|
|
772
|
+
});
|
|
773
|
+
xmtp.on("reaction", async ({ conversation, message }) => {
|
|
942
774
|
try {
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
775
|
+
const text = message.content.content;
|
|
776
|
+
const messages = [
|
|
777
|
+
{
|
|
778
|
+
id: (0, import_node_crypto2.randomUUID)(),
|
|
779
|
+
role: "user",
|
|
780
|
+
parts: [{ type: "text", text }]
|
|
781
|
+
}
|
|
782
|
+
];
|
|
783
|
+
const baseRuntime = {
|
|
784
|
+
conversation,
|
|
785
|
+
message,
|
|
786
|
+
xmtpClient
|
|
787
|
+
};
|
|
788
|
+
const runtime = await agent.createRuntimeContext(baseRuntime);
|
|
789
|
+
if (context.behaviors) {
|
|
790
|
+
const behaviorContext2 = {
|
|
791
|
+
runtime,
|
|
792
|
+
client: xmtpClient,
|
|
793
|
+
conversation,
|
|
794
|
+
message
|
|
795
|
+
};
|
|
796
|
+
await context.behaviors.executeBefore(behaviorContext2);
|
|
797
|
+
}
|
|
798
|
+
const { text: reply } = await agent.generate(messages, { runtime });
|
|
799
|
+
let behaviorContext;
|
|
800
|
+
if (context.behaviors) {
|
|
801
|
+
behaviorContext = {
|
|
802
|
+
runtime,
|
|
803
|
+
client: xmtpClient,
|
|
804
|
+
conversation,
|
|
805
|
+
message,
|
|
806
|
+
response: reply
|
|
807
|
+
};
|
|
808
|
+
await context.behaviors.executeAfter(behaviorContext);
|
|
809
|
+
} else {
|
|
810
|
+
behaviorContext = {
|
|
811
|
+
runtime,
|
|
812
|
+
client: xmtpClient,
|
|
813
|
+
conversation,
|
|
814
|
+
message,
|
|
815
|
+
response: reply
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
if (behaviorContext?.sendOptions?.filtered) {
|
|
819
|
+
import_utils2.logger.debug(
|
|
820
|
+
`\u{1F507} [XMTP Plugin] Skipping reaction response due to message being filtered`
|
|
948
821
|
);
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
await sendResponse(
|
|
825
|
+
conversation,
|
|
826
|
+
reply,
|
|
827
|
+
message.id,
|
|
828
|
+
behaviorContext
|
|
829
|
+
);
|
|
830
|
+
} catch (err) {
|
|
831
|
+
import_utils2.logger.error("\u274C Error handling reaction:", err);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
xmtp.on("reply", async ({ conversation, message }) => {
|
|
835
|
+
try {
|
|
836
|
+
const text = message.content.content;
|
|
837
|
+
const messages = [
|
|
838
|
+
{
|
|
839
|
+
id: (0, import_node_crypto2.randomUUID)(),
|
|
840
|
+
role: "user",
|
|
841
|
+
parts: [{ type: "text", text }]
|
|
842
|
+
}
|
|
843
|
+
];
|
|
844
|
+
const baseRuntime = {
|
|
845
|
+
conversation,
|
|
846
|
+
message,
|
|
847
|
+
xmtpClient
|
|
848
|
+
};
|
|
849
|
+
const runtime = await agent.createRuntimeContext(baseRuntime);
|
|
850
|
+
let behaviorContext;
|
|
851
|
+
if (context.behaviors) {
|
|
852
|
+
behaviorContext = {
|
|
853
|
+
runtime,
|
|
854
|
+
client: xmtpClient,
|
|
855
|
+
conversation,
|
|
856
|
+
message
|
|
857
|
+
};
|
|
858
|
+
await context.behaviors.executeBefore(behaviorContext);
|
|
859
|
+
if (behaviorContext.stopped) {
|
|
860
|
+
import_utils2.logger.debug(
|
|
861
|
+
`\u{1F507} [XMTP Plugin] Skipping reply response due to behavior chain being stopped`
|
|
862
|
+
);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
const { text: reply } = await agent.generate(messages, { runtime });
|
|
867
|
+
if (context.behaviors) {
|
|
868
|
+
if (!behaviorContext) {
|
|
869
|
+
behaviorContext = {
|
|
870
|
+
runtime,
|
|
871
|
+
client: xmtpClient,
|
|
872
|
+
conversation,
|
|
873
|
+
message,
|
|
874
|
+
response: reply
|
|
875
|
+
};
|
|
876
|
+
} else {
|
|
877
|
+
behaviorContext.response = reply;
|
|
878
|
+
}
|
|
879
|
+
await context.behaviors.executeAfter(behaviorContext);
|
|
880
|
+
if (behaviorContext.stopped) {
|
|
881
|
+
import_utils2.logger.debug(
|
|
882
|
+
`\u{1F507} [XMTP Plugin] Skipping reply response due to post-behavior chain being stopped`
|
|
883
|
+
);
|
|
884
|
+
return;
|
|
952
885
|
}
|
|
886
|
+
} else {
|
|
887
|
+
behaviorContext = {
|
|
888
|
+
runtime,
|
|
889
|
+
client: xmtpClient,
|
|
890
|
+
conversation,
|
|
891
|
+
message,
|
|
892
|
+
response: reply
|
|
893
|
+
};
|
|
953
894
|
}
|
|
954
|
-
|
|
955
|
-
|
|
895
|
+
await sendResponse(
|
|
896
|
+
conversation,
|
|
897
|
+
reply,
|
|
898
|
+
message.id,
|
|
899
|
+
behaviorContext
|
|
900
|
+
);
|
|
901
|
+
} catch (err) {
|
|
902
|
+
import_utils2.logger.error("\u274C Error handling reply:", err);
|
|
956
903
|
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
904
|
+
});
|
|
905
|
+
xmtp.on("text", async ({ conversation, message }) => {
|
|
906
|
+
try {
|
|
907
|
+
const text = message.content;
|
|
908
|
+
const messages = [
|
|
909
|
+
{ id: (0, import_node_crypto2.randomUUID)(), role: "user", parts: [{ type: "text", text }] }
|
|
910
|
+
];
|
|
911
|
+
const baseRuntime = {
|
|
912
|
+
conversation,
|
|
913
|
+
message,
|
|
914
|
+
xmtpClient
|
|
915
|
+
};
|
|
916
|
+
const runtime = await agent.createRuntimeContext(baseRuntime);
|
|
917
|
+
let behaviorContext;
|
|
918
|
+
if (context.behaviors) {
|
|
919
|
+
behaviorContext = {
|
|
920
|
+
runtime,
|
|
921
|
+
client: xmtpClient,
|
|
922
|
+
conversation,
|
|
923
|
+
message
|
|
924
|
+
};
|
|
925
|
+
await context.behaviors.executeBefore(behaviorContext);
|
|
926
|
+
if (behaviorContext.stopped) {
|
|
927
|
+
import_utils2.logger.debug(
|
|
928
|
+
`\u{1F507} [XMTP Plugin] Skipping text response due to behavior chain being stopped`
|
|
929
|
+
);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
const { text: reply } = await agent.generate(messages, { runtime });
|
|
934
|
+
if (context.behaviors) {
|
|
935
|
+
if (!behaviorContext) {
|
|
936
|
+
behaviorContext = {
|
|
937
|
+
runtime,
|
|
938
|
+
client: xmtpClient,
|
|
939
|
+
conversation,
|
|
940
|
+
message,
|
|
941
|
+
response: reply
|
|
942
|
+
};
|
|
943
|
+
} else {
|
|
944
|
+
behaviorContext.response = reply;
|
|
945
|
+
}
|
|
946
|
+
await context.behaviors.executeAfter(behaviorContext);
|
|
947
|
+
if (behaviorContext.stopped) {
|
|
948
|
+
import_utils2.logger.debug(
|
|
949
|
+
`\u{1F507} [XMTP Plugin] Skipping text response due to post-behavior chain being stopped`
|
|
950
|
+
);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
} else {
|
|
954
|
+
behaviorContext = {
|
|
955
|
+
runtime,
|
|
956
|
+
client: xmtpClient,
|
|
957
|
+
conversation,
|
|
958
|
+
message,
|
|
959
|
+
response: reply
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
await sendResponse(
|
|
963
|
+
conversation,
|
|
964
|
+
reply,
|
|
965
|
+
message.id,
|
|
966
|
+
behaviorContext
|
|
967
|
+
);
|
|
968
|
+
} catch (err) {
|
|
969
|
+
import_utils2.logger.error("\u274C Error handling text:", err);
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
void xmtp.start().then(() => import_utils2.logger.debug("\u2705 XMTP agent listener started")).catch(
|
|
973
|
+
(err) => console.error("\u274C XMTP agent listener failed to start:", err)
|
|
974
|
+
);
|
|
961
975
|
}
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
* Clear the cache
|
|
965
|
-
*/
|
|
966
|
-
clearCache() {
|
|
967
|
-
this.cache.clear();
|
|
968
|
-
console.log("\u{1F5D1}\uFE0F Address cache cleared");
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Get cache statistics
|
|
972
|
-
*/
|
|
973
|
-
getCacheStats() {
|
|
974
|
-
return {
|
|
975
|
-
size: this.cache.size,
|
|
976
|
-
maxSize: this.maxCacheSize
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
// src/resolver/basename-resolver.ts
|
|
982
|
-
var import_viem2 = require("viem");
|
|
983
|
-
var import_chains2 = require("viem/chains");
|
|
976
|
+
};
|
|
977
|
+
}
|
|
984
978
|
|
|
985
|
-
// src/
|
|
986
|
-
var
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
{ internalType: "address", name: "reverseRegistrar_", type: "address" },
|
|
996
|
-
{ internalType: "address", name: "owner_", type: "address" }
|
|
997
|
-
],
|
|
998
|
-
stateMutability: "nonpayable",
|
|
999
|
-
type: "constructor"
|
|
1000
|
-
},
|
|
1001
|
-
{ inputs: [], name: "AlreadyInitialized", type: "error" },
|
|
1002
|
-
{ inputs: [], name: "CantSetSelfAsDelegate", type: "error" },
|
|
1003
|
-
{ inputs: [], name: "CantSetSelfAsOperator", type: "error" },
|
|
1004
|
-
{ inputs: [], name: "NewOwnerIsZeroAddress", type: "error" },
|
|
1005
|
-
{ inputs: [], name: "NoHandoverRequest", type: "error" },
|
|
1006
|
-
{ inputs: [], name: "Unauthorized", type: "error" },
|
|
1007
|
-
{
|
|
1008
|
-
anonymous: false,
|
|
1009
|
-
inputs: [
|
|
1010
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1011
|
-
{
|
|
1012
|
-
indexed: true,
|
|
1013
|
-
internalType: "uint256",
|
|
1014
|
-
name: "contentType",
|
|
1015
|
-
type: "uint256"
|
|
1016
|
-
}
|
|
1017
|
-
],
|
|
1018
|
-
name: "ABIChanged",
|
|
1019
|
-
type: "event"
|
|
1020
|
-
},
|
|
1021
|
-
{
|
|
1022
|
-
anonymous: false,
|
|
1023
|
-
inputs: [
|
|
1024
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1025
|
-
{ indexed: false, internalType: "address", name: "a", type: "address" }
|
|
1026
|
-
],
|
|
1027
|
-
name: "AddrChanged",
|
|
1028
|
-
type: "event"
|
|
1029
|
-
},
|
|
1030
|
-
{
|
|
1031
|
-
anonymous: false,
|
|
1032
|
-
inputs: [
|
|
1033
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1034
|
-
{
|
|
1035
|
-
indexed: false,
|
|
1036
|
-
internalType: "uint256",
|
|
1037
|
-
name: "coinType",
|
|
1038
|
-
type: "uint256"
|
|
1039
|
-
},
|
|
1040
|
-
{
|
|
1041
|
-
indexed: false,
|
|
1042
|
-
internalType: "bytes",
|
|
1043
|
-
name: "newAddress",
|
|
1044
|
-
type: "bytes"
|
|
1045
|
-
}
|
|
1046
|
-
],
|
|
1047
|
-
name: "AddressChanged",
|
|
1048
|
-
type: "event"
|
|
1049
|
-
},
|
|
1050
|
-
{
|
|
1051
|
-
anonymous: false,
|
|
1052
|
-
inputs: [
|
|
1053
|
-
{
|
|
1054
|
-
indexed: true,
|
|
1055
|
-
internalType: "address",
|
|
1056
|
-
name: "owner",
|
|
1057
|
-
type: "address"
|
|
1058
|
-
},
|
|
1059
|
-
{
|
|
1060
|
-
indexed: true,
|
|
1061
|
-
internalType: "address",
|
|
1062
|
-
name: "operator",
|
|
1063
|
-
type: "address"
|
|
1064
|
-
},
|
|
1065
|
-
{ indexed: false, internalType: "bool", name: "approved", type: "bool" }
|
|
1066
|
-
],
|
|
1067
|
-
name: "ApprovalForAll",
|
|
1068
|
-
type: "event"
|
|
1069
|
-
},
|
|
1070
|
-
{
|
|
1071
|
-
anonymous: false,
|
|
1072
|
-
inputs: [
|
|
1073
|
-
{
|
|
1074
|
-
indexed: false,
|
|
1075
|
-
internalType: "address",
|
|
1076
|
-
name: "owner",
|
|
1077
|
-
type: "address"
|
|
1078
|
-
},
|
|
1079
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1080
|
-
{
|
|
1081
|
-
indexed: true,
|
|
1082
|
-
internalType: "address",
|
|
1083
|
-
name: "delegate",
|
|
1084
|
-
type: "address"
|
|
1085
|
-
},
|
|
1086
|
-
{ indexed: true, internalType: "bool", name: "approved", type: "bool" }
|
|
1087
|
-
],
|
|
1088
|
-
name: "Approved",
|
|
1089
|
-
type: "event"
|
|
1090
|
-
},
|
|
1091
|
-
{
|
|
1092
|
-
anonymous: false,
|
|
1093
|
-
inputs: [
|
|
1094
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1095
|
-
{ indexed: false, internalType: "bytes", name: "hash", type: "bytes" }
|
|
1096
|
-
],
|
|
1097
|
-
name: "ContenthashChanged",
|
|
1098
|
-
type: "event"
|
|
1099
|
-
},
|
|
1100
|
-
{
|
|
1101
|
-
anonymous: false,
|
|
1102
|
-
inputs: [
|
|
1103
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1104
|
-
{ indexed: false, internalType: "bytes", name: "name", type: "bytes" },
|
|
1105
|
-
{
|
|
1106
|
-
indexed: false,
|
|
1107
|
-
internalType: "uint16",
|
|
1108
|
-
name: "resource",
|
|
1109
|
-
type: "uint16"
|
|
1110
|
-
},
|
|
1111
|
-
{ indexed: false, internalType: "bytes", name: "record", type: "bytes" }
|
|
1112
|
-
],
|
|
1113
|
-
name: "DNSRecordChanged",
|
|
1114
|
-
type: "event"
|
|
1115
|
-
},
|
|
1116
|
-
{
|
|
1117
|
-
anonymous: false,
|
|
1118
|
-
inputs: [
|
|
1119
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1120
|
-
{ indexed: false, internalType: "bytes", name: "name", type: "bytes" },
|
|
1121
|
-
{
|
|
1122
|
-
indexed: false,
|
|
1123
|
-
internalType: "uint16",
|
|
1124
|
-
name: "resource",
|
|
1125
|
-
type: "uint16"
|
|
1126
|
-
}
|
|
1127
|
-
],
|
|
1128
|
-
name: "DNSRecordDeleted",
|
|
1129
|
-
type: "event"
|
|
1130
|
-
},
|
|
1131
|
-
{
|
|
1132
|
-
anonymous: false,
|
|
1133
|
-
inputs: [
|
|
1134
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1135
|
-
{
|
|
1136
|
-
indexed: false,
|
|
1137
|
-
internalType: "bytes",
|
|
1138
|
-
name: "lastzonehash",
|
|
1139
|
-
type: "bytes"
|
|
1140
|
-
},
|
|
1141
|
-
{
|
|
1142
|
-
indexed: false,
|
|
1143
|
-
internalType: "bytes",
|
|
1144
|
-
name: "zonehash",
|
|
1145
|
-
type: "bytes"
|
|
1146
|
-
}
|
|
1147
|
-
],
|
|
1148
|
-
name: "DNSZonehashChanged",
|
|
1149
|
-
type: "event"
|
|
1150
|
-
},
|
|
1151
|
-
{
|
|
1152
|
-
anonymous: false,
|
|
1153
|
-
inputs: [
|
|
1154
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1155
|
-
{
|
|
1156
|
-
indexed: true,
|
|
1157
|
-
internalType: "bytes4",
|
|
1158
|
-
name: "interfaceID",
|
|
1159
|
-
type: "bytes4"
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
indexed: false,
|
|
1163
|
-
internalType: "address",
|
|
1164
|
-
name: "implementer",
|
|
1165
|
-
type: "address"
|
|
1166
|
-
}
|
|
1167
|
-
],
|
|
1168
|
-
name: "InterfaceChanged",
|
|
1169
|
-
type: "event"
|
|
1170
|
-
},
|
|
1171
|
-
{
|
|
1172
|
-
anonymous: false,
|
|
1173
|
-
inputs: [
|
|
1174
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1175
|
-
{ indexed: false, internalType: "string", name: "name", type: "string" }
|
|
1176
|
-
],
|
|
1177
|
-
name: "NameChanged",
|
|
1178
|
-
type: "event"
|
|
1179
|
-
},
|
|
1180
|
-
{
|
|
1181
|
-
anonymous: false,
|
|
1182
|
-
inputs: [
|
|
1183
|
-
{
|
|
1184
|
-
indexed: true,
|
|
1185
|
-
internalType: "address",
|
|
1186
|
-
name: "pendingOwner",
|
|
1187
|
-
type: "address"
|
|
1188
|
-
}
|
|
1189
|
-
],
|
|
1190
|
-
name: "OwnershipHandoverCanceled",
|
|
1191
|
-
type: "event"
|
|
1192
|
-
},
|
|
1193
|
-
{
|
|
1194
|
-
anonymous: false,
|
|
1195
|
-
inputs: [
|
|
1196
|
-
{
|
|
1197
|
-
indexed: true,
|
|
1198
|
-
internalType: "address",
|
|
1199
|
-
name: "pendingOwner",
|
|
1200
|
-
type: "address"
|
|
1201
|
-
}
|
|
1202
|
-
],
|
|
1203
|
-
name: "OwnershipHandoverRequested",
|
|
1204
|
-
type: "event"
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
anonymous: false,
|
|
1208
|
-
inputs: [
|
|
1209
|
-
{
|
|
1210
|
-
indexed: true,
|
|
1211
|
-
internalType: "address",
|
|
1212
|
-
name: "oldOwner",
|
|
1213
|
-
type: "address"
|
|
1214
|
-
},
|
|
1215
|
-
{
|
|
1216
|
-
indexed: true,
|
|
1217
|
-
internalType: "address",
|
|
1218
|
-
name: "newOwner",
|
|
1219
|
-
type: "address"
|
|
1220
|
-
}
|
|
1221
|
-
],
|
|
1222
|
-
name: "OwnershipTransferred",
|
|
1223
|
-
type: "event"
|
|
1224
|
-
},
|
|
1225
|
-
{
|
|
1226
|
-
anonymous: false,
|
|
1227
|
-
inputs: [
|
|
1228
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1229
|
-
{ indexed: false, internalType: "bytes32", name: "x", type: "bytes32" },
|
|
1230
|
-
{ indexed: false, internalType: "bytes32", name: "y", type: "bytes32" }
|
|
1231
|
-
],
|
|
1232
|
-
name: "PubkeyChanged",
|
|
1233
|
-
type: "event"
|
|
1234
|
-
},
|
|
1235
|
-
{
|
|
1236
|
-
anonymous: false,
|
|
1237
|
-
inputs: [
|
|
1238
|
-
{
|
|
1239
|
-
indexed: true,
|
|
1240
|
-
internalType: "address",
|
|
1241
|
-
name: "newRegistrarController",
|
|
1242
|
-
type: "address"
|
|
1243
|
-
}
|
|
1244
|
-
],
|
|
1245
|
-
name: "RegistrarControllerUpdated",
|
|
1246
|
-
type: "event"
|
|
1247
|
-
},
|
|
1248
|
-
{
|
|
1249
|
-
anonymous: false,
|
|
1250
|
-
inputs: [
|
|
1251
|
-
{
|
|
1252
|
-
indexed: true,
|
|
1253
|
-
internalType: "address",
|
|
1254
|
-
name: "newReverseRegistrar",
|
|
1255
|
-
type: "address"
|
|
1256
|
-
}
|
|
1257
|
-
],
|
|
1258
|
-
name: "ReverseRegistrarUpdated",
|
|
1259
|
-
type: "event"
|
|
1260
|
-
},
|
|
1261
|
-
{
|
|
1262
|
-
anonymous: false,
|
|
1263
|
-
inputs: [
|
|
1264
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1265
|
-
{
|
|
1266
|
-
indexed: true,
|
|
1267
|
-
internalType: "string",
|
|
1268
|
-
name: "indexedKey",
|
|
1269
|
-
type: "string"
|
|
1270
|
-
},
|
|
1271
|
-
{ indexed: false, internalType: "string", name: "key", type: "string" },
|
|
1272
|
-
{ indexed: false, internalType: "string", name: "value", type: "string" }
|
|
1273
|
-
],
|
|
1274
|
-
name: "TextChanged",
|
|
1275
|
-
type: "event"
|
|
1276
|
-
},
|
|
1277
|
-
{
|
|
1278
|
-
anonymous: false,
|
|
1279
|
-
inputs: [
|
|
1280
|
-
{ indexed: true, internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1281
|
-
{
|
|
1282
|
-
indexed: false,
|
|
1283
|
-
internalType: "uint64",
|
|
1284
|
-
name: "newVersion",
|
|
1285
|
-
type: "uint64"
|
|
1286
|
-
}
|
|
1287
|
-
],
|
|
1288
|
-
name: "VersionChanged",
|
|
1289
|
-
type: "event"
|
|
1290
|
-
},
|
|
1291
|
-
{
|
|
1292
|
-
inputs: [
|
|
1293
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1294
|
-
{ internalType: "uint256", name: "contentTypes", type: "uint256" }
|
|
1295
|
-
],
|
|
1296
|
-
name: "ABI",
|
|
1297
|
-
outputs: [
|
|
1298
|
-
{ internalType: "uint256", name: "", type: "uint256" },
|
|
1299
|
-
{ internalType: "bytes", name: "", type: "bytes" }
|
|
1300
|
-
],
|
|
1301
|
-
stateMutability: "view",
|
|
1302
|
-
type: "function"
|
|
1303
|
-
},
|
|
1304
|
-
{
|
|
1305
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1306
|
-
name: "addr",
|
|
1307
|
-
outputs: [{ internalType: "address payable", name: "", type: "address" }],
|
|
1308
|
-
stateMutability: "view",
|
|
1309
|
-
type: "function"
|
|
1310
|
-
},
|
|
1311
|
-
{
|
|
1312
|
-
inputs: [
|
|
1313
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1314
|
-
{ internalType: "uint256", name: "coinType", type: "uint256" }
|
|
1315
|
-
],
|
|
1316
|
-
name: "addr",
|
|
1317
|
-
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1318
|
-
stateMutability: "view",
|
|
1319
|
-
type: "function"
|
|
1320
|
-
},
|
|
1321
|
-
{
|
|
1322
|
-
inputs: [
|
|
1323
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1324
|
-
{ internalType: "address", name: "delegate", type: "address" },
|
|
1325
|
-
{ internalType: "bool", name: "approved", type: "bool" }
|
|
1326
|
-
],
|
|
1327
|
-
name: "approve",
|
|
1328
|
-
outputs: [],
|
|
1329
|
-
stateMutability: "nonpayable",
|
|
1330
|
-
type: "function"
|
|
1331
|
-
},
|
|
1332
|
-
{
|
|
1333
|
-
inputs: [],
|
|
1334
|
-
name: "cancelOwnershipHandover",
|
|
1335
|
-
outputs: [],
|
|
1336
|
-
stateMutability: "payable",
|
|
1337
|
-
type: "function"
|
|
1338
|
-
},
|
|
1339
|
-
{
|
|
1340
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1341
|
-
name: "clearRecords",
|
|
1342
|
-
outputs: [],
|
|
1343
|
-
stateMutability: "nonpayable",
|
|
1344
|
-
type: "function"
|
|
1345
|
-
},
|
|
1346
|
-
{
|
|
1347
|
-
inputs: [
|
|
1348
|
-
{ internalType: "address", name: "pendingOwner", type: "address" }
|
|
1349
|
-
],
|
|
1350
|
-
name: "completeOwnershipHandover",
|
|
1351
|
-
outputs: [],
|
|
1352
|
-
stateMutability: "payable",
|
|
1353
|
-
type: "function"
|
|
1354
|
-
},
|
|
1355
|
-
{
|
|
1356
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1357
|
-
name: "contenthash",
|
|
1358
|
-
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1359
|
-
stateMutability: "view",
|
|
1360
|
-
type: "function"
|
|
1361
|
-
},
|
|
1362
|
-
{
|
|
1363
|
-
inputs: [
|
|
1364
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1365
|
-
{ internalType: "bytes32", name: "name", type: "bytes32" },
|
|
1366
|
-
{ internalType: "uint16", name: "resource", type: "uint16" }
|
|
1367
|
-
],
|
|
1368
|
-
name: "dnsRecord",
|
|
1369
|
-
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1370
|
-
stateMutability: "view",
|
|
1371
|
-
type: "function"
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
inputs: [],
|
|
1375
|
-
name: "ens",
|
|
1376
|
-
outputs: [{ internalType: "contract ENS", name: "", type: "address" }],
|
|
1377
|
-
stateMutability: "view",
|
|
1378
|
-
type: "function"
|
|
1379
|
-
},
|
|
1380
|
-
{
|
|
1381
|
-
inputs: [
|
|
1382
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1383
|
-
{ internalType: "bytes32", name: "name", type: "bytes32" }
|
|
1384
|
-
],
|
|
1385
|
-
name: "hasDNSRecords",
|
|
1386
|
-
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1387
|
-
stateMutability: "view",
|
|
1388
|
-
type: "function"
|
|
1389
|
-
},
|
|
1390
|
-
{
|
|
1391
|
-
inputs: [
|
|
1392
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1393
|
-
{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }
|
|
1394
|
-
],
|
|
1395
|
-
name: "interfaceImplementer",
|
|
1396
|
-
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1397
|
-
stateMutability: "view",
|
|
1398
|
-
type: "function"
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
inputs: [
|
|
1402
|
-
{ internalType: "address", name: "owner", type: "address" },
|
|
1403
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1404
|
-
{ internalType: "address", name: "delegate", type: "address" }
|
|
1405
|
-
],
|
|
1406
|
-
name: "isApprovedFor",
|
|
1407
|
-
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1408
|
-
stateMutability: "view",
|
|
1409
|
-
type: "function"
|
|
1410
|
-
},
|
|
1411
|
-
{
|
|
1412
|
-
inputs: [
|
|
1413
|
-
{ internalType: "address", name: "account", type: "address" },
|
|
1414
|
-
{ internalType: "address", name: "operator", type: "address" }
|
|
1415
|
-
],
|
|
1416
|
-
name: "isApprovedForAll",
|
|
1417
|
-
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1418
|
-
stateMutability: "view",
|
|
1419
|
-
type: "function"
|
|
1420
|
-
},
|
|
1421
|
-
{
|
|
1422
|
-
inputs: [{ internalType: "bytes[]", name: "data", type: "bytes[]" }],
|
|
1423
|
-
name: "multicall",
|
|
1424
|
-
outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }],
|
|
1425
|
-
stateMutability: "nonpayable",
|
|
1426
|
-
type: "function"
|
|
1427
|
-
},
|
|
1428
|
-
{
|
|
1429
|
-
inputs: [
|
|
1430
|
-
{ internalType: "bytes32", name: "nodehash", type: "bytes32" },
|
|
1431
|
-
{ internalType: "bytes[]", name: "data", type: "bytes[]" }
|
|
1432
|
-
],
|
|
1433
|
-
name: "multicallWithNodeCheck",
|
|
1434
|
-
outputs: [{ internalType: "bytes[]", name: "results", type: "bytes[]" }],
|
|
1435
|
-
stateMutability: "nonpayable",
|
|
1436
|
-
type: "function"
|
|
1437
|
-
},
|
|
1438
|
-
{
|
|
1439
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1440
|
-
name: "name",
|
|
1441
|
-
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1442
|
-
stateMutability: "view",
|
|
1443
|
-
type: "function"
|
|
1444
|
-
},
|
|
1445
|
-
{
|
|
1446
|
-
inputs: [],
|
|
1447
|
-
name: "owner",
|
|
1448
|
-
outputs: [{ internalType: "address", name: "result", type: "address" }],
|
|
1449
|
-
stateMutability: "view",
|
|
1450
|
-
type: "function"
|
|
1451
|
-
},
|
|
1452
|
-
{
|
|
1453
|
-
inputs: [
|
|
1454
|
-
{ internalType: "address", name: "pendingOwner", type: "address" }
|
|
1455
|
-
],
|
|
1456
|
-
name: "ownershipHandoverExpiresAt",
|
|
1457
|
-
outputs: [{ internalType: "uint256", name: "result", type: "uint256" }],
|
|
1458
|
-
stateMutability: "view",
|
|
1459
|
-
type: "function"
|
|
1460
|
-
},
|
|
1461
|
-
{
|
|
1462
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1463
|
-
name: "pubkey",
|
|
1464
|
-
outputs: [
|
|
1465
|
-
{ internalType: "bytes32", name: "x", type: "bytes32" },
|
|
1466
|
-
{ internalType: "bytes32", name: "y", type: "bytes32" }
|
|
1467
|
-
],
|
|
1468
|
-
stateMutability: "view",
|
|
1469
|
-
type: "function"
|
|
1470
|
-
},
|
|
1471
|
-
{
|
|
1472
|
-
inputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
|
|
1473
|
-
name: "recordVersions",
|
|
1474
|
-
outputs: [{ internalType: "uint64", name: "", type: "uint64" }],
|
|
1475
|
-
stateMutability: "view",
|
|
1476
|
-
type: "function"
|
|
1477
|
-
},
|
|
1478
|
-
{
|
|
1479
|
-
inputs: [],
|
|
1480
|
-
name: "registrarController",
|
|
1481
|
-
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1482
|
-
stateMutability: "view",
|
|
1483
|
-
type: "function"
|
|
1484
|
-
},
|
|
1485
|
-
{
|
|
1486
|
-
inputs: [],
|
|
1487
|
-
name: "renounceOwnership",
|
|
1488
|
-
outputs: [],
|
|
1489
|
-
stateMutability: "payable",
|
|
1490
|
-
type: "function"
|
|
1491
|
-
},
|
|
1492
|
-
{
|
|
1493
|
-
inputs: [],
|
|
1494
|
-
name: "requestOwnershipHandover",
|
|
1495
|
-
outputs: [],
|
|
1496
|
-
stateMutability: "payable",
|
|
1497
|
-
type: "function"
|
|
1498
|
-
},
|
|
1499
|
-
{
|
|
1500
|
-
inputs: [
|
|
1501
|
-
{ internalType: "bytes", name: "", type: "bytes" },
|
|
1502
|
-
{ internalType: "bytes", name: "data", type: "bytes" }
|
|
1503
|
-
],
|
|
1504
|
-
name: "resolve",
|
|
1505
|
-
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1506
|
-
stateMutability: "view",
|
|
1507
|
-
type: "function"
|
|
1508
|
-
},
|
|
1509
|
-
{
|
|
1510
|
-
inputs: [],
|
|
1511
|
-
name: "reverseRegistrar",
|
|
1512
|
-
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1513
|
-
stateMutability: "view",
|
|
1514
|
-
type: "function"
|
|
1515
|
-
},
|
|
1516
|
-
{
|
|
1517
|
-
inputs: [
|
|
1518
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1519
|
-
{ internalType: "uint256", name: "contentType", type: "uint256" },
|
|
1520
|
-
{ internalType: "bytes", name: "data", type: "bytes" }
|
|
1521
|
-
],
|
|
1522
|
-
name: "setABI",
|
|
1523
|
-
outputs: [],
|
|
1524
|
-
stateMutability: "nonpayable",
|
|
1525
|
-
type: "function"
|
|
1526
|
-
},
|
|
1527
|
-
{
|
|
1528
|
-
inputs: [
|
|
1529
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1530
|
-
{ internalType: "uint256", name: "coinType", type: "uint256" },
|
|
1531
|
-
{ internalType: "bytes", name: "a", type: "bytes" }
|
|
1532
|
-
],
|
|
1533
|
-
name: "setAddr",
|
|
1534
|
-
outputs: [],
|
|
1535
|
-
stateMutability: "nonpayable",
|
|
1536
|
-
type: "function"
|
|
1537
|
-
},
|
|
1538
|
-
{
|
|
1539
|
-
inputs: [
|
|
1540
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1541
|
-
{ internalType: "address", name: "a", type: "address" }
|
|
1542
|
-
],
|
|
1543
|
-
name: "setAddr",
|
|
1544
|
-
outputs: [],
|
|
1545
|
-
stateMutability: "nonpayable",
|
|
1546
|
-
type: "function"
|
|
1547
|
-
},
|
|
1548
|
-
{
|
|
1549
|
-
inputs: [
|
|
1550
|
-
{ internalType: "address", name: "operator", type: "address" },
|
|
1551
|
-
{ internalType: "bool", name: "approved", type: "bool" }
|
|
1552
|
-
],
|
|
1553
|
-
name: "setApprovalForAll",
|
|
1554
|
-
outputs: [],
|
|
1555
|
-
stateMutability: "nonpayable",
|
|
1556
|
-
type: "function"
|
|
1557
|
-
},
|
|
1558
|
-
{
|
|
1559
|
-
inputs: [
|
|
1560
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1561
|
-
{ internalType: "bytes", name: "hash", type: "bytes" }
|
|
1562
|
-
],
|
|
1563
|
-
name: "setContenthash",
|
|
1564
|
-
outputs: [],
|
|
1565
|
-
stateMutability: "nonpayable",
|
|
1566
|
-
type: "function"
|
|
1567
|
-
},
|
|
1568
|
-
{
|
|
1569
|
-
inputs: [
|
|
1570
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1571
|
-
{ internalType: "bytes", name: "data", type: "bytes" }
|
|
1572
|
-
],
|
|
1573
|
-
name: "setDNSRecords",
|
|
1574
|
-
outputs: [],
|
|
1575
|
-
stateMutability: "nonpayable",
|
|
1576
|
-
type: "function"
|
|
1577
|
-
},
|
|
1578
|
-
{
|
|
1579
|
-
inputs: [
|
|
1580
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1581
|
-
{ internalType: "bytes4", name: "interfaceID", type: "bytes4" },
|
|
1582
|
-
{ internalType: "address", name: "implementer", type: "address" }
|
|
1583
|
-
],
|
|
1584
|
-
name: "setInterface",
|
|
1585
|
-
outputs: [],
|
|
1586
|
-
stateMutability: "nonpayable",
|
|
1587
|
-
type: "function"
|
|
1588
|
-
},
|
|
1589
|
-
{
|
|
1590
|
-
inputs: [
|
|
1591
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1592
|
-
{ internalType: "string", name: "newName", type: "string" }
|
|
1593
|
-
],
|
|
1594
|
-
name: "setName",
|
|
1595
|
-
outputs: [],
|
|
1596
|
-
stateMutability: "nonpayable",
|
|
1597
|
-
type: "function"
|
|
1598
|
-
},
|
|
1599
|
-
{
|
|
1600
|
-
inputs: [
|
|
1601
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1602
|
-
{ internalType: "bytes32", name: "x", type: "bytes32" },
|
|
1603
|
-
{ internalType: "bytes32", name: "y", type: "bytes32" }
|
|
1604
|
-
],
|
|
1605
|
-
name: "setPubkey",
|
|
1606
|
-
outputs: [],
|
|
1607
|
-
stateMutability: "nonpayable",
|
|
1608
|
-
type: "function"
|
|
1609
|
-
},
|
|
1610
|
-
{
|
|
1611
|
-
inputs: [
|
|
1612
|
-
{
|
|
1613
|
-
internalType: "address",
|
|
1614
|
-
name: "registrarController_",
|
|
1615
|
-
type: "address"
|
|
1616
|
-
}
|
|
1617
|
-
],
|
|
1618
|
-
name: "setRegistrarController",
|
|
1619
|
-
outputs: [],
|
|
1620
|
-
stateMutability: "nonpayable",
|
|
1621
|
-
type: "function"
|
|
1622
|
-
},
|
|
1623
|
-
{
|
|
1624
|
-
inputs: [
|
|
1625
|
-
{ internalType: "address", name: "reverseRegistrar_", type: "address" }
|
|
1626
|
-
],
|
|
1627
|
-
name: "setReverseRegistrar",
|
|
1628
|
-
outputs: [],
|
|
1629
|
-
stateMutability: "nonpayable",
|
|
1630
|
-
type: "function"
|
|
1631
|
-
},
|
|
1632
|
-
{
|
|
1633
|
-
inputs: [
|
|
1634
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1635
|
-
{ internalType: "string", name: "key", type: "string" },
|
|
1636
|
-
{ internalType: "string", name: "value", type: "string" }
|
|
1637
|
-
],
|
|
1638
|
-
name: "setText",
|
|
1639
|
-
outputs: [],
|
|
1640
|
-
stateMutability: "nonpayable",
|
|
1641
|
-
type: "function"
|
|
1642
|
-
},
|
|
1643
|
-
{
|
|
1644
|
-
inputs: [
|
|
1645
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1646
|
-
{ internalType: "bytes", name: "hash", type: "bytes" }
|
|
1647
|
-
],
|
|
1648
|
-
name: "setZonehash",
|
|
1649
|
-
outputs: [],
|
|
1650
|
-
stateMutability: "nonpayable",
|
|
1651
|
-
type: "function"
|
|
1652
|
-
},
|
|
1653
|
-
{
|
|
1654
|
-
inputs: [{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }],
|
|
1655
|
-
name: "supportsInterface",
|
|
1656
|
-
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1657
|
-
stateMutability: "view",
|
|
1658
|
-
type: "function"
|
|
1659
|
-
},
|
|
1660
|
-
{
|
|
1661
|
-
inputs: [
|
|
1662
|
-
{ internalType: "bytes32", name: "node", type: "bytes32" },
|
|
1663
|
-
{ internalType: "string", name: "key", type: "string" }
|
|
1664
|
-
],
|
|
1665
|
-
name: "text",
|
|
1666
|
-
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1667
|
-
stateMutability: "view",
|
|
1668
|
-
type: "function"
|
|
1669
|
-
},
|
|
1670
|
-
{
|
|
1671
|
-
inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
|
|
1672
|
-
name: "transferOwnership",
|
|
1673
|
-
outputs: [],
|
|
1674
|
-
stateMutability: "payable",
|
|
1675
|
-
type: "function"
|
|
1676
|
-
},
|
|
1677
|
-
{
|
|
1678
|
-
inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
|
|
1679
|
-
name: "zonehash",
|
|
1680
|
-
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1681
|
-
stateMutability: "view",
|
|
1682
|
-
type: "function"
|
|
979
|
+
// src/lib/jwt.ts
|
|
980
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
|
|
981
|
+
var import_utils3 = require("@hybrd/utils");
|
|
982
|
+
function getJwtSecret() {
|
|
983
|
+
const secret = process.env.XMTP_DB_ENCRYPTION_KEY;
|
|
984
|
+
const nodeEnv = process.env.NODE_ENV || "development";
|
|
985
|
+
if (nodeEnv === "production" && !secret) {
|
|
986
|
+
throw new Error(
|
|
987
|
+
"XMTP_DB_ENCRYPTION_KEY environment variable is required in production. Generate a secure random secret for JWT token signing."
|
|
988
|
+
);
|
|
1683
989
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
Email: "email",
|
|
1690
|
-
Url: "url",
|
|
1691
|
-
Avatar: "avatar",
|
|
1692
|
-
Description: "description",
|
|
1693
|
-
Notice: "notice",
|
|
1694
|
-
Keywords: "keywords",
|
|
1695
|
-
Twitter: "com.twitter",
|
|
1696
|
-
Github: "com.github",
|
|
1697
|
-
Discord: "com.discord",
|
|
1698
|
-
Telegram: "org.telegram",
|
|
1699
|
-
Snapshot: "snapshot",
|
|
1700
|
-
Location: "location"
|
|
1701
|
-
};
|
|
1702
|
-
var convertChainIdToCoinType = (chainId) => {
|
|
1703
|
-
if (chainId === import_chains2.mainnet.id) {
|
|
1704
|
-
return "addr";
|
|
990
|
+
if (!secret) {
|
|
991
|
+
import_utils3.logger.warn(
|
|
992
|
+
"\u26A0\uFE0F [SECURITY] Using fallback JWT secret for development. Set XMTP_DB_ENCRYPTION_KEY environment variable for production."
|
|
993
|
+
);
|
|
994
|
+
return "fallback-secret-for-dev-only";
|
|
1705
995
|
}
|
|
1706
|
-
|
|
1707
|
-
return cointype.toString(16).toLocaleUpperCase();
|
|
1708
|
-
};
|
|
1709
|
-
var convertReverseNodeToBytes = (address, chainId) => {
|
|
1710
|
-
const addressFormatted = address.toLocaleLowerCase();
|
|
1711
|
-
const addressNode = (0, import_viem2.keccak256)(addressFormatted.substring(2));
|
|
1712
|
-
const chainCoinType = convertChainIdToCoinType(chainId);
|
|
1713
|
-
const baseReverseNode = (0, import_viem2.namehash)(
|
|
1714
|
-
`${chainCoinType.toLocaleUpperCase()}.reverse`
|
|
1715
|
-
);
|
|
1716
|
-
const addressReverseNode = (0, import_viem2.keccak256)(
|
|
1717
|
-
(0, import_viem2.encodePacked)(["bytes32", "bytes32"], [baseReverseNode, addressNode])
|
|
1718
|
-
);
|
|
1719
|
-
return addressReverseNode;
|
|
1720
|
-
};
|
|
1721
|
-
function convertBasenameToNode(basename) {
|
|
1722
|
-
return (0, import_viem2.namehash)(basename);
|
|
996
|
+
return secret;
|
|
1723
997
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
/**
|
|
1743
|
-
* Initialize the resolver address based on the client's chain ID
|
|
1744
|
-
*/
|
|
1745
|
-
async initializeResolver() {
|
|
1746
|
-
if (this.resolverAddress && this.chainId) {
|
|
1747
|
-
console.log(
|
|
1748
|
-
`\u{1F504} BasenameResolver already initialized for chain ${this.chainId} with resolver ${this.resolverAddress}`
|
|
1749
|
-
);
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
try {
|
|
1753
|
-
console.log("\u{1F504} Initializing BasenameResolver...");
|
|
1754
|
-
this.chainId = await this.baseClient.getChainId();
|
|
1755
|
-
console.log(`\u{1F517} Chain ID detected: ${this.chainId}`);
|
|
1756
|
-
this.resolverAddress = getResolverAddress();
|
|
1757
|
-
console.log(
|
|
1758
|
-
`\u{1F4CD} Resolver address for chain ${this.chainId}: ${this.resolverAddress}`
|
|
1759
|
-
);
|
|
1760
|
-
console.log(
|
|
1761
|
-
`\u2705 Initialized BasenameResolver for chain ${this.chainId} with resolver ${this.resolverAddress}`
|
|
1762
|
-
);
|
|
1763
|
-
} catch (error) {
|
|
1764
|
-
console.error("\u274C Failed to initialize BasenameResolver:", error);
|
|
1765
|
-
throw error;
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
/**
|
|
1769
|
-
* Get the resolver address, initializing if necessary
|
|
1770
|
-
*/
|
|
1771
|
-
async getResolverAddress() {
|
|
1772
|
-
await this.initializeResolver();
|
|
1773
|
-
if (!this.resolverAddress) {
|
|
1774
|
-
throw new Error("Failed to initialize resolver address");
|
|
1775
|
-
}
|
|
1776
|
-
return this.resolverAddress;
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Resolve a basename from an Ethereum address
|
|
1780
|
-
*/
|
|
1781
|
-
async getBasename(address) {
|
|
1782
|
-
console.log(`\u{1F50D} Starting basename resolution for address: ${address}`);
|
|
1783
|
-
try {
|
|
1784
|
-
const cached = this.getCachedBasename(address);
|
|
1785
|
-
if (cached) {
|
|
1786
|
-
console.log(`\u2705 Resolved basename from cache: ${cached}`);
|
|
1787
|
-
return cached;
|
|
1788
|
-
}
|
|
1789
|
-
console.log(`\u{1F4ED} No cached basename found for address: ${address}`);
|
|
1790
|
-
console.log("\u{1F504} Getting resolver address...");
|
|
1791
|
-
const resolverAddress = await this.getResolverAddress();
|
|
1792
|
-
console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
|
|
1793
|
-
console.log("\u{1F504} Getting chain ID...");
|
|
1794
|
-
const chainId = await this.baseClient.getChainId();
|
|
1795
|
-
console.log(`\u{1F517} Chain ID: ${chainId}`);
|
|
1796
|
-
console.log("\u{1F504} Converting address to reverse node...");
|
|
1797
|
-
const addressReverseNode = convertReverseNodeToBytes(
|
|
1798
|
-
// address.toUpperCase() as `0x${string}`,
|
|
1799
|
-
address,
|
|
1800
|
-
chainId
|
|
1801
|
-
);
|
|
1802
|
-
console.log(`\u{1F517} Reverse node: ${addressReverseNode}`);
|
|
1803
|
-
console.log("\u{1F504} Reading contract to resolve basename...");
|
|
1804
|
-
const basename = await this.baseClient.readContract({
|
|
1805
|
-
abi: L2ResolverAbi,
|
|
1806
|
-
address: resolverAddress,
|
|
1807
|
-
functionName: "name",
|
|
1808
|
-
args: [addressReverseNode]
|
|
1809
|
-
});
|
|
1810
|
-
console.log(
|
|
1811
|
-
`\u{1F4CB} Contract returned basename: "${basename}" (length: ${basename?.length || 0})`
|
|
1812
|
-
);
|
|
1813
|
-
if (basename && basename.length > 0) {
|
|
1814
|
-
this.setCachedBasename(address, basename);
|
|
1815
|
-
console.log(`\u2705 Resolved basename: ${basename} for address: ${address}`);
|
|
1816
|
-
return basename;
|
|
1817
|
-
}
|
|
1818
|
-
console.log(
|
|
1819
|
-
`\u274C No basename found for address: ${address} (empty or null response)`
|
|
1820
|
-
);
|
|
1821
|
-
return null;
|
|
1822
|
-
} catch (error) {
|
|
1823
|
-
console.error(
|
|
1824
|
-
`\u274C Error resolving basename for address ${address}:`,
|
|
1825
|
-
error
|
|
1826
|
-
);
|
|
1827
|
-
if (error instanceof Error) {
|
|
1828
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
1829
|
-
console.error(`\u274C Error stack:`, error.stack);
|
|
1830
|
-
}
|
|
1831
|
-
return null;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
/**
|
|
1835
|
-
* Get the avatar URL for a basename
|
|
1836
|
-
*/
|
|
1837
|
-
async getBasenameAvatar(basename) {
|
|
1838
|
-
console.log(`\u{1F5BC}\uFE0F Getting avatar for basename: ${basename}`);
|
|
1839
|
-
return this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Avatar);
|
|
1840
|
-
}
|
|
1841
|
-
/**
|
|
1842
|
-
* Get a text record for a basename
|
|
1843
|
-
*/
|
|
1844
|
-
async getBasenameTextRecord(basename, key) {
|
|
1845
|
-
console.log(`\u{1F4DD} Getting text record "${key}" for basename: ${basename}`);
|
|
1846
|
-
try {
|
|
1847
|
-
const cached = this.getCachedTextRecord(basename, key);
|
|
1848
|
-
if (cached) {
|
|
1849
|
-
console.log(`\u2705 Resolved text record from cache: ${key}=${cached}`);
|
|
1850
|
-
return cached;
|
|
1851
|
-
}
|
|
1852
|
-
console.log(`\u{1F4ED} No cached text record found for ${basename}.${key}`);
|
|
1853
|
-
console.log("\u{1F504} Getting resolver address...");
|
|
1854
|
-
const resolverAddress = await this.getResolverAddress();
|
|
1855
|
-
console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
|
|
1856
|
-
console.log("\u{1F504} Converting basename to node...");
|
|
1857
|
-
const node = convertBasenameToNode(basename);
|
|
1858
|
-
console.log(`\u{1F517} Node hash: ${node}`);
|
|
1859
|
-
console.log(`\u{1F504} Reading contract for text record "${key}"...`);
|
|
1860
|
-
const textRecord = await this.baseClient.readContract({
|
|
1861
|
-
abi: L2ResolverAbi,
|
|
1862
|
-
address: resolverAddress,
|
|
1863
|
-
functionName: "text",
|
|
1864
|
-
args: [node, key]
|
|
1865
|
-
});
|
|
1866
|
-
console.log(
|
|
1867
|
-
`\u{1F4CB} Contract returned text record: "${textRecord}" (length: ${textRecord?.length || 0})`
|
|
1868
|
-
);
|
|
1869
|
-
if (textRecord && textRecord.length > 0) {
|
|
1870
|
-
this.setCachedTextRecord(basename, key, textRecord);
|
|
1871
|
-
console.log(`\u2705 Resolved text record: ${key}=${textRecord}`);
|
|
1872
|
-
return textRecord;
|
|
1873
|
-
}
|
|
1874
|
-
console.log(
|
|
1875
|
-
`\u274C No text record found for ${basename}.${key} (empty or null response)`
|
|
1876
|
-
);
|
|
1877
|
-
return null;
|
|
1878
|
-
} catch (error) {
|
|
1879
|
-
console.error(
|
|
1880
|
-
`\u274C Error resolving text record ${key} for ${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
|
-
* Get the Ethereum address that owns a basename
|
|
1892
|
-
*/
|
|
1893
|
-
async getBasenameAddress(basename) {
|
|
1894
|
-
console.log(`\u{1F50D} Getting address for basename: ${basename}`);
|
|
1895
|
-
try {
|
|
1896
|
-
console.log("\u{1F504} Getting resolver address...");
|
|
1897
|
-
const resolverAddress = await this.getResolverAddress();
|
|
1898
|
-
console.log(`\u{1F4CD} Using resolver address: ${resolverAddress}`);
|
|
1899
|
-
console.log("\u{1F504} Converting basename to node...");
|
|
1900
|
-
const node = convertBasenameToNode(basename);
|
|
1901
|
-
console.log(`\u{1F517} Node hash: ${node}`);
|
|
1902
|
-
console.log("\u{1F504} Reading contract to resolve address...");
|
|
1903
|
-
const address = await this.baseClient.readContract({
|
|
1904
|
-
abi: L2ResolverAbi,
|
|
1905
|
-
address: resolverAddress,
|
|
1906
|
-
functionName: "addr",
|
|
1907
|
-
args: [node]
|
|
1908
|
-
});
|
|
1909
|
-
console.log(`\u{1F4CB} Contract returned address: "${address}"`);
|
|
1910
|
-
if (address && address !== "0x0000000000000000000000000000000000000000") {
|
|
1911
|
-
console.log(`\u2705 Resolved address: ${address} for basename: ${basename}`);
|
|
1912
|
-
return address;
|
|
1913
|
-
}
|
|
1914
|
-
console.log(
|
|
1915
|
-
`\u274C No address found for basename: ${basename} (zero address or null response)`
|
|
1916
|
-
);
|
|
1917
|
-
return null;
|
|
1918
|
-
} catch (error) {
|
|
1919
|
-
console.error(
|
|
1920
|
-
`\u274C Error resolving address for basename ${basename}:`,
|
|
1921
|
-
error
|
|
1922
|
-
);
|
|
1923
|
-
if (error instanceof Error) {
|
|
1924
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
1925
|
-
console.error(`\u274C Error stack:`, error.stack);
|
|
1926
|
-
}
|
|
1927
|
-
return null;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
/**
|
|
1931
|
-
* Get all basic metadata for a basename
|
|
1932
|
-
*/
|
|
1933
|
-
async getBasenameMetadata(basename) {
|
|
1934
|
-
console.log(`\u{1F4CA} Getting metadata for basename: ${basename}`);
|
|
1935
|
-
try {
|
|
1936
|
-
const [avatar, description, twitter, github, url] = await Promise.all([
|
|
1937
|
-
this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Avatar),
|
|
1938
|
-
this.getBasenameTextRecord(
|
|
1939
|
-
basename,
|
|
1940
|
-
BasenameTextRecordKeys.Description
|
|
1941
|
-
),
|
|
1942
|
-
this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Twitter),
|
|
1943
|
-
this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Github),
|
|
1944
|
-
this.getBasenameTextRecord(basename, BasenameTextRecordKeys.Url)
|
|
1945
|
-
]);
|
|
1946
|
-
const metadata = {
|
|
1947
|
-
basename,
|
|
1948
|
-
avatar,
|
|
1949
|
-
description,
|
|
1950
|
-
twitter,
|
|
1951
|
-
github,
|
|
1952
|
-
url
|
|
1953
|
-
};
|
|
1954
|
-
console.log(`\u2705 Resolved metadata for ${basename}:`, metadata);
|
|
1955
|
-
return metadata;
|
|
1956
|
-
} catch (error) {
|
|
1957
|
-
console.error(
|
|
1958
|
-
`\u274C Error resolving metadata for basename ${basename}:`,
|
|
1959
|
-
error
|
|
1960
|
-
);
|
|
1961
|
-
if (error instanceof Error) {
|
|
1962
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
1963
|
-
console.error(`\u274C Error stack:`, error.stack);
|
|
1964
|
-
}
|
|
1965
|
-
return null;
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
/**
|
|
1969
|
-
* Resolve a full basename profile (name + metadata) from an address
|
|
1970
|
-
*/
|
|
1971
|
-
async resolveBasenameProfile(address) {
|
|
1972
|
-
console.log(`\u{1F464} Resolving full basename profile for address: ${address}`);
|
|
1973
|
-
try {
|
|
1974
|
-
const basename = await this.getBasename(address);
|
|
1975
|
-
if (!basename) {
|
|
1976
|
-
console.log(`\u274C No basename found for address: ${address}`);
|
|
1977
|
-
return null;
|
|
1978
|
-
}
|
|
1979
|
-
console.log(`\u{1F504} Getting metadata for resolved basename: ${basename}`);
|
|
1980
|
-
const metadata = await this.getBasenameMetadata(basename);
|
|
1981
|
-
const profile = {
|
|
1982
|
-
address,
|
|
1983
|
-
...metadata
|
|
1984
|
-
};
|
|
1985
|
-
console.log(`\u2705 Resolved full profile for ${address}:`, profile);
|
|
1986
|
-
return profile;
|
|
1987
|
-
} catch (error) {
|
|
1988
|
-
console.error(
|
|
1989
|
-
`\u274C Error resolving basename profile for ${address}:`,
|
|
1990
|
-
error
|
|
1991
|
-
);
|
|
1992
|
-
if (error instanceof Error) {
|
|
1993
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
1994
|
-
console.error(`\u274C Error stack:`, error.stack);
|
|
1995
|
-
}
|
|
1996
|
-
return null;
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
/**
|
|
2000
|
-
* Get cached basename if not expired
|
|
2001
|
-
*/
|
|
2002
|
-
getCachedBasename(address) {
|
|
2003
|
-
const entry = this.cache.get(address.toLowerCase());
|
|
2004
|
-
if (!entry) {
|
|
2005
|
-
console.log(
|
|
2006
|
-
`\u{1F4ED} No cache entry found for address: ${address.toLowerCase()}`
|
|
2007
|
-
);
|
|
2008
|
-
return null;
|
|
2009
|
-
}
|
|
2010
|
-
const now = Date.now();
|
|
2011
|
-
const age = now - entry.timestamp;
|
|
2012
|
-
console.log(`\u{1F550} Cache entry age: ${age}ms (TTL: ${this.cacheTtl}ms)`);
|
|
2013
|
-
if (age > this.cacheTtl) {
|
|
2014
|
-
console.log(
|
|
2015
|
-
`\u23F0 Cache entry expired for address: ${address.toLowerCase()}`
|
|
2016
|
-
);
|
|
2017
|
-
this.cache.delete(address.toLowerCase());
|
|
2018
|
-
return null;
|
|
2019
|
-
}
|
|
2020
|
-
console.log(
|
|
2021
|
-
`\u2705 Valid cache entry found for ${address.toLowerCase()}: "${entry.basename}"`
|
|
2022
|
-
);
|
|
2023
|
-
return entry.basename;
|
|
2024
|
-
}
|
|
2025
|
-
/**
|
|
2026
|
-
* Cache basename with LRU eviction
|
|
2027
|
-
*/
|
|
2028
|
-
setCachedBasename(address, basename) {
|
|
2029
|
-
if (this.cache.size >= this.maxCacheSize) {
|
|
2030
|
-
const firstKey = this.cache.keys().next().value;
|
|
2031
|
-
if (firstKey) {
|
|
2032
|
-
console.log(`\u{1F5D1}\uFE0F Cache full, removing oldest entry: ${firstKey}`);
|
|
2033
|
-
this.cache.delete(firstKey);
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
console.log(
|
|
2037
|
-
`\u{1F4BE} Caching basename "${basename}" for address: ${address.toLowerCase()}`
|
|
2038
|
-
);
|
|
2039
|
-
this.cache.set(address.toLowerCase(), {
|
|
2040
|
-
basename,
|
|
2041
|
-
timestamp: Date.now()
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
/**
|
|
2045
|
-
* Get cached text record if not expired
|
|
2046
|
-
*/
|
|
2047
|
-
getCachedTextRecord(basename, key) {
|
|
2048
|
-
const basenameCache = this.textRecordCache.get(basename);
|
|
2049
|
-
if (!basenameCache) {
|
|
2050
|
-
console.log(`\u{1F4ED} No text record cache found for basename: ${basename}`);
|
|
2051
|
-
return null;
|
|
2052
|
-
}
|
|
2053
|
-
const entry = basenameCache.get(key);
|
|
2054
|
-
if (!entry) {
|
|
2055
|
-
console.log(`\u{1F4ED} No cached text record found for ${basename}.${key}`);
|
|
2056
|
-
return null;
|
|
2057
|
-
}
|
|
2058
|
-
const now = Date.now();
|
|
2059
|
-
const age = now - entry.timestamp;
|
|
2060
|
-
console.log(
|
|
2061
|
-
`\u{1F550} Text record cache entry age: ${age}ms (TTL: ${this.cacheTtl}ms)`
|
|
2062
|
-
);
|
|
2063
|
-
if (age > this.cacheTtl) {
|
|
2064
|
-
console.log(`\u23F0 Text record cache entry expired for ${basename}.${key}`);
|
|
2065
|
-
basenameCache.delete(key);
|
|
2066
|
-
return null;
|
|
2067
|
-
}
|
|
2068
|
-
console.log(
|
|
2069
|
-
`\u2705 Valid text record cache entry found for ${basename}.${key}: "${entry.value}"`
|
|
2070
|
-
);
|
|
2071
|
-
return entry.value;
|
|
2072
|
-
}
|
|
2073
|
-
/**
|
|
2074
|
-
* Cache text record
|
|
2075
|
-
*/
|
|
2076
|
-
setCachedTextRecord(basename, key, value) {
|
|
2077
|
-
let basenameCache = this.textRecordCache.get(basename);
|
|
2078
|
-
if (!basenameCache) {
|
|
2079
|
-
console.log(`\u{1F4DD} Creating new text record cache for basename: ${basename}`);
|
|
2080
|
-
basenameCache = /* @__PURE__ */ new Map();
|
|
2081
|
-
this.textRecordCache.set(basename, basenameCache);
|
|
2082
|
-
}
|
|
2083
|
-
console.log(
|
|
2084
|
-
`\u{1F4BE} Caching text record "${key}" = "${value}" for basename: ${basename}`
|
|
2085
|
-
);
|
|
2086
|
-
basenameCache.set(key, {
|
|
2087
|
-
value,
|
|
2088
|
-
timestamp: Date.now()
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
/**
|
|
2092
|
-
* Clear all caches
|
|
2093
|
-
*/
|
|
2094
|
-
clearCache() {
|
|
2095
|
-
const basenameCount = this.cache.size;
|
|
2096
|
-
const textRecordCount = this.textRecordCache.size;
|
|
2097
|
-
this.cache.clear();
|
|
2098
|
-
this.textRecordCache.clear();
|
|
2099
|
-
console.log(`\u{1F5D1}\uFE0F Basename cache cleared (${basenameCount} entries removed)`);
|
|
2100
|
-
console.log(
|
|
2101
|
-
`\u{1F5D1}\uFE0F Text record cache cleared (${textRecordCount} basename caches removed)`
|
|
2102
|
-
);
|
|
2103
|
-
}
|
|
2104
|
-
/**
|
|
2105
|
-
* Get cache statistics
|
|
2106
|
-
*/
|
|
2107
|
-
getCacheStats() {
|
|
2108
|
-
return {
|
|
2109
|
-
basenameCache: {
|
|
2110
|
-
size: this.cache.size,
|
|
2111
|
-
maxSize: this.maxCacheSize
|
|
2112
|
-
},
|
|
2113
|
-
textRecordCache: {
|
|
2114
|
-
size: this.textRecordCache.size
|
|
2115
|
-
},
|
|
2116
|
-
chainId: this.chainId,
|
|
2117
|
-
resolverAddress: this.resolverAddress
|
|
2118
|
-
};
|
|
2119
|
-
}
|
|
2120
|
-
};
|
|
2121
|
-
|
|
2122
|
-
// src/resolver/ens-resolver.ts
|
|
2123
|
-
var ENSResolver = class {
|
|
2124
|
-
constructor(options) {
|
|
2125
|
-
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
2126
|
-
__publicField(this, "reverseCache", /* @__PURE__ */ new Map());
|
|
2127
|
-
__publicField(this, "maxCacheSize");
|
|
2128
|
-
__publicField(this, "cacheTtl");
|
|
2129
|
-
__publicField(this, "mainnetClient");
|
|
2130
|
-
this.maxCacheSize = options.maxCacheSize ?? 500;
|
|
2131
|
-
this.cacheTtl = options.cacheTtl ?? 36e5;
|
|
2132
|
-
this.mainnetClient = options.mainnetClient;
|
|
2133
|
-
}
|
|
2134
|
-
/**
|
|
2135
|
-
* Resolve an ENS name to an Ethereum address
|
|
2136
|
-
*/
|
|
2137
|
-
async resolveENSName(ensName) {
|
|
2138
|
-
console.log(`\u{1F50D} Resolving ENS name: ${ensName}`);
|
|
2139
|
-
try {
|
|
2140
|
-
const cached = this.getCachedAddress(ensName);
|
|
2141
|
-
if (cached) {
|
|
2142
|
-
console.log(`\u2705 Resolved ENS from cache: ${ensName} \u2192 ${cached}`);
|
|
2143
|
-
return cached;
|
|
2144
|
-
}
|
|
2145
|
-
console.log(`\u{1F4ED} No cached address found for ENS: ${ensName}`);
|
|
2146
|
-
console.log("\u{1F504} Reading ENS contract...");
|
|
2147
|
-
const address = await this.mainnetClient.getEnsAddress({
|
|
2148
|
-
name: ensName
|
|
2149
|
-
});
|
|
2150
|
-
console.log(`\u{1F4CB} ENS contract returned address: "${address}"`);
|
|
2151
|
-
if (address && address !== "0x0000000000000000000000000000000000000000") {
|
|
2152
|
-
this.setCachedAddress(ensName, address);
|
|
2153
|
-
console.log(`\u2705 Resolved ENS: ${ensName} \u2192 ${address}`);
|
|
2154
|
-
return address;
|
|
2155
|
-
}
|
|
2156
|
-
console.log(`\u274C No address found for ENS: ${ensName}`);
|
|
2157
|
-
return null;
|
|
2158
|
-
} catch (error) {
|
|
2159
|
-
console.error(`\u274C Error resolving ENS name ${ensName}:`, error);
|
|
2160
|
-
if (error instanceof Error) {
|
|
2161
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
2162
|
-
}
|
|
2163
|
-
return null;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
/**
|
|
2167
|
-
* Resolve an address to its primary ENS name (reverse resolution)
|
|
2168
|
-
*/
|
|
2169
|
-
async resolveAddressToENS(address) {
|
|
2170
|
-
console.log(`\u{1F50D} Reverse resolving address to ENS: ${address}`);
|
|
2171
|
-
try {
|
|
2172
|
-
const cached = this.getCachedENSName(address);
|
|
2173
|
-
if (cached) {
|
|
2174
|
-
console.log(
|
|
2175
|
-
`\u2705 Resolved ENS from reverse cache: ${address} \u2192 ${cached}`
|
|
2176
|
-
);
|
|
2177
|
-
return cached;
|
|
2178
|
-
}
|
|
2179
|
-
console.log(`\u{1F4ED} No cached ENS name found for address: ${address}`);
|
|
2180
|
-
console.log("\u{1F504} Reading ENS reverse resolver...");
|
|
2181
|
-
const ensName = await this.mainnetClient.getEnsName({
|
|
2182
|
-
address
|
|
2183
|
-
});
|
|
2184
|
-
console.log(`\u{1F4CB} ENS reverse resolver returned: "${ensName}"`);
|
|
2185
|
-
if (ensName && ensName.length > 0) {
|
|
2186
|
-
this.setCachedENSName(address, ensName);
|
|
2187
|
-
console.log(`\u2705 Reverse resolved: ${address} \u2192 ${ensName}`);
|
|
2188
|
-
return ensName;
|
|
2189
|
-
}
|
|
2190
|
-
console.log(`\u274C No ENS name found for address: ${address}`);
|
|
2191
|
-
return null;
|
|
2192
|
-
} catch (error) {
|
|
2193
|
-
console.error(`\u274C Error reverse resolving address ${address}:`, error);
|
|
2194
|
-
if (error instanceof Error) {
|
|
2195
|
-
console.error(`\u274C Error details: ${error.message}`);
|
|
2196
|
-
}
|
|
2197
|
-
return null;
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
/**
|
|
2201
|
-
* Get ENS avatar for a name
|
|
2202
|
-
*/
|
|
2203
|
-
async getENSAvatar(ensName) {
|
|
2204
|
-
console.log(`\u{1F5BC}\uFE0F Getting ENS avatar for: ${ensName}`);
|
|
2205
|
-
try {
|
|
2206
|
-
const avatar = await this.mainnetClient.getEnsAvatar({
|
|
2207
|
-
name: ensName
|
|
2208
|
-
});
|
|
2209
|
-
if (avatar) {
|
|
2210
|
-
console.log(`\u2705 Found ENS avatar: ${avatar}`);
|
|
2211
|
-
return avatar;
|
|
2212
|
-
}
|
|
2213
|
-
console.log(`\u274C No avatar found for ENS: ${ensName}`);
|
|
2214
|
-
return null;
|
|
2215
|
-
} catch (error) {
|
|
2216
|
-
console.error(`\u274C Error getting ENS avatar for ${ensName}:`, error);
|
|
2217
|
-
return null;
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
/**
|
|
2221
|
-
* Get ENS text record
|
|
2222
|
-
*/
|
|
2223
|
-
async getENSTextRecord(ensName, key) {
|
|
2224
|
-
console.log(`\u{1F4DD} Getting ENS text record "${key}" for: ${ensName}`);
|
|
2225
|
-
try {
|
|
2226
|
-
const textRecord = await this.mainnetClient.getEnsText({
|
|
2227
|
-
name: ensName,
|
|
2228
|
-
key
|
|
2229
|
-
});
|
|
2230
|
-
if (textRecord && textRecord.length > 0) {
|
|
2231
|
-
console.log(`\u2705 Found ENS text record: ${key}=${textRecord}`);
|
|
2232
|
-
return textRecord;
|
|
2233
|
-
}
|
|
2234
|
-
console.log(`\u274C No text record "${key}" found for ENS: ${ensName}`);
|
|
2235
|
-
return null;
|
|
2236
|
-
} catch (error) {
|
|
2237
|
-
console.error(
|
|
2238
|
-
`\u274C Error getting ENS text record ${key} for ${ensName}:`,
|
|
2239
|
-
error
|
|
2240
|
-
);
|
|
2241
|
-
return null;
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
/**
|
|
2245
|
-
* Get comprehensive ENS profile
|
|
2246
|
-
*/
|
|
2247
|
-
async getENSProfile(ensName) {
|
|
2248
|
-
console.log(`\u{1F464} Getting ENS profile for: ${ensName}`);
|
|
2249
|
-
try {
|
|
2250
|
-
const [address, avatar, description, twitter, github, url] = await Promise.all([
|
|
2251
|
-
this.resolveENSName(ensName),
|
|
2252
|
-
this.getENSAvatar(ensName),
|
|
2253
|
-
this.getENSTextRecord(ensName, "description"),
|
|
2254
|
-
this.getENSTextRecord(ensName, "com.twitter"),
|
|
2255
|
-
this.getENSTextRecord(ensName, "com.github"),
|
|
2256
|
-
this.getENSTextRecord(ensName, "url")
|
|
2257
|
-
]);
|
|
2258
|
-
const profile = {
|
|
2259
|
-
ensName,
|
|
2260
|
-
address,
|
|
2261
|
-
avatar,
|
|
2262
|
-
description,
|
|
2263
|
-
twitter,
|
|
2264
|
-
github,
|
|
2265
|
-
url
|
|
2266
|
-
};
|
|
2267
|
-
console.log(`\u2705 ENS profile for ${ensName}:`, profile);
|
|
2268
|
-
return profile;
|
|
2269
|
-
} catch (error) {
|
|
2270
|
-
console.error(`\u274C Error getting ENS profile for ${ensName}:`, error);
|
|
2271
|
-
return null;
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
/**
|
|
2275
|
-
* Check if a name is a valid ENS name (.eth)
|
|
2276
|
-
*/
|
|
2277
|
-
isENSName(name) {
|
|
2278
|
-
return name.endsWith(".eth") && !name.endsWith(".base.eth");
|
|
2279
|
-
}
|
|
2280
|
-
/**
|
|
2281
|
-
* Get cached address if not expired
|
|
2282
|
-
*/
|
|
2283
|
-
getCachedAddress(ensName) {
|
|
2284
|
-
const entry = this.cache.get(ensName.toLowerCase());
|
|
2285
|
-
if (!entry) {
|
|
2286
|
-
return null;
|
|
2287
|
-
}
|
|
2288
|
-
const now = Date.now();
|
|
2289
|
-
if (now - entry.timestamp > this.cacheTtl) {
|
|
2290
|
-
this.cache.delete(ensName.toLowerCase());
|
|
2291
|
-
return null;
|
|
2292
|
-
}
|
|
2293
|
-
return entry.address;
|
|
2294
|
-
}
|
|
2295
|
-
/**
|
|
2296
|
-
* Cache address with LRU eviction
|
|
2297
|
-
*/
|
|
2298
|
-
setCachedAddress(ensName, address) {
|
|
2299
|
-
if (this.cache.size >= this.maxCacheSize) {
|
|
2300
|
-
const firstKey = this.cache.keys().next().value;
|
|
2301
|
-
if (firstKey) {
|
|
2302
|
-
this.cache.delete(firstKey);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
this.cache.set(ensName.toLowerCase(), {
|
|
2306
|
-
address,
|
|
2307
|
-
timestamp: Date.now()
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
/**
|
|
2311
|
-
* Get cached ENS name if not expired
|
|
2312
|
-
*/
|
|
2313
|
-
getCachedENSName(address) {
|
|
2314
|
-
const entry = this.reverseCache.get(address.toLowerCase());
|
|
2315
|
-
if (!entry) {
|
|
2316
|
-
return null;
|
|
2317
|
-
}
|
|
2318
|
-
const now = Date.now();
|
|
2319
|
-
if (now - entry.timestamp > this.cacheTtl) {
|
|
2320
|
-
this.reverseCache.delete(address.toLowerCase());
|
|
2321
|
-
return null;
|
|
2322
|
-
}
|
|
2323
|
-
return entry.ensName;
|
|
2324
|
-
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Cache ENS name with LRU eviction
|
|
2327
|
-
*/
|
|
2328
|
-
setCachedENSName(address, ensName) {
|
|
2329
|
-
if (this.reverseCache.size >= this.maxCacheSize) {
|
|
2330
|
-
const firstKey = this.reverseCache.keys().next().value;
|
|
2331
|
-
if (firstKey) {
|
|
2332
|
-
this.reverseCache.delete(firstKey);
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
this.reverseCache.set(address.toLowerCase(), {
|
|
2336
|
-
ensName,
|
|
2337
|
-
timestamp: Date.now()
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
/**
|
|
2341
|
-
* Clear all caches
|
|
2342
|
-
*/
|
|
2343
|
-
clearCache() {
|
|
2344
|
-
const addressCount = this.cache.size;
|
|
2345
|
-
const ensCount = this.reverseCache.size;
|
|
2346
|
-
this.cache.clear();
|
|
2347
|
-
this.reverseCache.clear();
|
|
2348
|
-
console.log(`\u{1F5D1}\uFE0F ENS address cache cleared (${addressCount} entries removed)`);
|
|
2349
|
-
console.log(`\u{1F5D1}\uFE0F ENS reverse cache cleared (${ensCount} entries removed)`);
|
|
2350
|
-
}
|
|
2351
|
-
/**
|
|
2352
|
-
* Get cache statistics
|
|
2353
|
-
*/
|
|
2354
|
-
getCacheStats() {
|
|
2355
|
-
return {
|
|
2356
|
-
addressCache: {
|
|
2357
|
-
size: this.cache.size,
|
|
2358
|
-
maxSize: this.maxCacheSize
|
|
2359
|
-
},
|
|
2360
|
-
reverseCache: {
|
|
2361
|
-
size: this.reverseCache.size,
|
|
2362
|
-
maxSize: this.maxCacheSize
|
|
2363
|
-
}
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
};
|
|
2367
|
-
|
|
2368
|
-
// src/resolver/xmtp-resolver.ts
|
|
2369
|
-
var XmtpResolver = class {
|
|
2370
|
-
constructor(client, options = {}) {
|
|
2371
|
-
this.client = client;
|
|
2372
|
-
__publicField(this, "addressCache", /* @__PURE__ */ new Map());
|
|
2373
|
-
__publicField(this, "messageCache", /* @__PURE__ */ new Map());
|
|
2374
|
-
__publicField(this, "maxCacheSize");
|
|
2375
|
-
__publicField(this, "cacheTtl");
|
|
2376
|
-
__publicField(this, "maxMessageCacheSize");
|
|
2377
|
-
__publicField(this, "messageCacheTtl");
|
|
2378
|
-
this.maxCacheSize = options.maxCacheSize ?? 1e3;
|
|
2379
|
-
this.cacheTtl = options.cacheTtl ?? 864e5;
|
|
2380
|
-
this.maxMessageCacheSize = options.maxMessageCacheSize ?? 1e3;
|
|
2381
|
-
this.messageCacheTtl = options.messageCacheTtl ?? 36e5;
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Resolve user address from inbox ID with caching
|
|
2385
|
-
*/
|
|
2386
|
-
async resolveAddress(inboxId, conversationId) {
|
|
2387
|
-
const cached = this.getCachedAddress(inboxId);
|
|
2388
|
-
if (cached) {
|
|
2389
|
-
console.log(
|
|
2390
|
-
`\u2705 [XmtpResolver] Resolved user address from cache: ${cached}`
|
|
2391
|
-
);
|
|
2392
|
-
return cached;
|
|
2393
|
-
}
|
|
2394
|
-
let userAddress = void 0;
|
|
2395
|
-
try {
|
|
2396
|
-
if (conversationId) {
|
|
2397
|
-
const conversation = await this.client.conversations.getConversationById(conversationId);
|
|
2398
|
-
if (conversation) {
|
|
2399
|
-
userAddress = await this.resolveFromConversation(
|
|
2400
|
-
conversation,
|
|
2401
|
-
inboxId
|
|
2402
|
-
);
|
|
2403
|
-
if (userAddress) {
|
|
2404
|
-
this.setCachedAddress(inboxId, userAddress);
|
|
2405
|
-
console.log(
|
|
2406
|
-
`\u2705 [XmtpResolver] Resolved user address: ${userAddress}`
|
|
2407
|
-
);
|
|
2408
|
-
return userAddress;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
userAddress = await this.resolveFromInboxState(inboxId);
|
|
2413
|
-
if (userAddress) {
|
|
2414
|
-
this.setCachedAddress(inboxId, userAddress);
|
|
2415
|
-
console.log(
|
|
2416
|
-
`\u2705 [XmtpResolver] Resolved user address via fallback: ${userAddress}`
|
|
2417
|
-
);
|
|
2418
|
-
return userAddress;
|
|
2419
|
-
}
|
|
2420
|
-
console.log(`\u26A0\uFE0F [XmtpResolver] No identifiers found for inbox ${inboxId}`);
|
|
2421
|
-
return null;
|
|
2422
|
-
} catch (error) {
|
|
2423
|
-
console.error(
|
|
2424
|
-
`\u274C [XmtpResolver] Error resolving user address for ${inboxId}:`,
|
|
2425
|
-
error
|
|
2426
|
-
);
|
|
2427
|
-
return null;
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
/**
|
|
2431
|
-
* Find any message by ID with caching
|
|
2432
|
-
*/
|
|
2433
|
-
async findMessage(messageId) {
|
|
2434
|
-
const cached = this.getCachedMessage(messageId);
|
|
2435
|
-
if (cached !== void 0) {
|
|
2436
|
-
console.log(
|
|
2437
|
-
cached ? `\u2705 [XmtpResolver] Found message from cache: ${cached.id}` : `\u2705 [XmtpResolver] Found cached null message for: ${messageId}`
|
|
2438
|
-
);
|
|
2439
|
-
return cached;
|
|
2440
|
-
}
|
|
2441
|
-
try {
|
|
2442
|
-
console.log(`\u{1F50D} [XmtpResolver] Finding message: ${messageId}`);
|
|
2443
|
-
const message = await this.client.conversations.getMessageById(messageId);
|
|
2444
|
-
if (message) {
|
|
2445
|
-
this.setCachedMessage(messageId, message);
|
|
2446
|
-
console.log(`\u2705 [XmtpResolver] Found and cached message: ${message.id}`);
|
|
2447
|
-
return message;
|
|
2448
|
-
}
|
|
2449
|
-
console.log(`\u26A0\uFE0F [XmtpResolver] Message not found: ${messageId}`);
|
|
2450
|
-
this.setCachedMessage(messageId, null);
|
|
2451
|
-
return null;
|
|
2452
|
-
} catch (error) {
|
|
2453
|
-
console.error(
|
|
2454
|
-
`\u274C [XmtpResolver] Error finding message ${messageId}:`,
|
|
2455
|
-
error
|
|
2456
|
-
);
|
|
2457
|
-
this.setCachedMessage(messageId, null);
|
|
2458
|
-
return null;
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
/**
|
|
2462
|
-
* Find root message with caching
|
|
2463
|
-
*/
|
|
2464
|
-
async findRootMessage(messageId) {
|
|
2465
|
-
const rootCacheKey = `root:${messageId}`;
|
|
2466
|
-
const cached = this.getCachedMessage(rootCacheKey);
|
|
2467
|
-
if (cached !== void 0) {
|
|
2468
|
-
console.log(
|
|
2469
|
-
cached ? `\u2705 [XmtpResolver] Found root message from cache: ${cached.id}` : `\u2705 [XmtpResolver] Found cached null root for: ${messageId}`
|
|
2470
|
-
);
|
|
2471
|
-
return cached;
|
|
2472
|
-
}
|
|
2473
|
-
try {
|
|
2474
|
-
console.log(`\u{1F50D} [XmtpResolver] Finding root message for: ${messageId}`);
|
|
2475
|
-
const rootMessage = await this.findRootMessageRecursive(messageId);
|
|
2476
|
-
if (rootMessage) {
|
|
2477
|
-
this.setCachedMessage(rootCacheKey, rootMessage);
|
|
2478
|
-
console.log(
|
|
2479
|
-
`\u2705 [XmtpResolver] Found and cached root message: ${rootMessage.id}`
|
|
2480
|
-
);
|
|
2481
|
-
return rootMessage;
|
|
2482
|
-
}
|
|
2483
|
-
console.log(`\u26A0\uFE0F [XmtpResolver] No root message found for: ${messageId}`);
|
|
2484
|
-
this.setCachedMessage(rootCacheKey, null);
|
|
2485
|
-
return null;
|
|
2486
|
-
} catch (error) {
|
|
2487
|
-
console.error(
|
|
2488
|
-
`\u274C [XmtpResolver] Error finding root message for ${messageId}:`,
|
|
2489
|
-
error
|
|
2490
|
-
);
|
|
2491
|
-
this.setCachedMessage(rootCacheKey, null);
|
|
2492
|
-
return null;
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
/**
|
|
2496
|
-
* Recursively finds the root message in a reply chain by following reply references
|
|
2497
|
-
*/
|
|
2498
|
-
async findRootMessageRecursive(messageId, visitedIds = /* @__PURE__ */ new Set()) {
|
|
2499
|
-
if (visitedIds.has(messageId)) {
|
|
2500
|
-
console.warn(
|
|
2501
|
-
`\u26A0\uFE0F Circular reference detected in message chain at ${messageId}`
|
|
2502
|
-
);
|
|
2503
|
-
return null;
|
|
2504
|
-
}
|
|
2505
|
-
visitedIds.add(messageId);
|
|
2506
|
-
const message = await this.client.conversations.getMessageById(messageId);
|
|
2507
|
-
if (!message) {
|
|
2508
|
-
console.warn(`\u26A0\uFE0F [findRootMessage] Message not found: ${messageId}`);
|
|
2509
|
-
return null;
|
|
2510
|
-
}
|
|
2511
|
-
console.log(`\u{1F50D} [findRootMessage] Raw message ${messageId}:`, {
|
|
2512
|
-
id: message.id,
|
|
2513
|
-
contentType: message.contentType,
|
|
2514
|
-
content: message.content,
|
|
2515
|
-
sentAt: message.sentAt
|
|
2516
|
-
});
|
|
2517
|
-
if (message.content?.reference) {
|
|
2518
|
-
return this.findRootMessageRecursive(
|
|
2519
|
-
message.content.reference,
|
|
2520
|
-
visitedIds
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2523
|
-
return message;
|
|
2524
|
-
}
|
|
2525
|
-
/**
|
|
2526
|
-
* Resolve address from conversation members
|
|
2527
|
-
*/
|
|
2528
|
-
async resolveFromConversation(conversation, inboxId) {
|
|
2529
|
-
try {
|
|
2530
|
-
const members = await conversation.members();
|
|
2531
|
-
const sender = members.find(
|
|
2532
|
-
(member) => member.inboxId.toLowerCase() === inboxId.toLowerCase()
|
|
2533
|
-
);
|
|
2534
|
-
if (sender) {
|
|
2535
|
-
const ethIdentifier = sender.accountIdentifiers.find(
|
|
2536
|
-
(id) => id.identifierKind === 0
|
|
2537
|
-
// IdentifierKind.Ethereum
|
|
2538
|
-
);
|
|
2539
|
-
if (ethIdentifier) {
|
|
2540
|
-
return ethIdentifier.identifier;
|
|
2541
|
-
} else {
|
|
2542
|
-
console.log(
|
|
2543
|
-
`\u26A0\uFE0F [XmtpResolver] No Ethereum identifier found for inbox ${inboxId}`
|
|
2544
|
-
);
|
|
2545
|
-
}
|
|
2546
|
-
} else {
|
|
2547
|
-
console.log(
|
|
2548
|
-
`\u26A0\uFE0F [XmtpResolver] Sender not found in conversation members for inbox ${inboxId}`
|
|
2549
|
-
);
|
|
2550
|
-
}
|
|
2551
|
-
} catch (error) {
|
|
2552
|
-
console.error(
|
|
2553
|
-
`\u274C [XmtpResolver] Error resolving from conversation members:`,
|
|
2554
|
-
error
|
|
2555
|
-
);
|
|
2556
|
-
}
|
|
2557
|
-
return null;
|
|
2558
|
-
}
|
|
2559
|
-
/**
|
|
2560
|
-
* Resolve address from inbox state (network fallback)
|
|
2561
|
-
*/
|
|
2562
|
-
async resolveFromInboxState(inboxId) {
|
|
2563
|
-
try {
|
|
2564
|
-
const inboxState = await this.client.preferences.inboxStateFromInboxIds([
|
|
2565
|
-
inboxId
|
|
2566
|
-
]);
|
|
2567
|
-
const firstState = inboxState?.[0];
|
|
2568
|
-
if (firstState?.identifiers && firstState.identifiers.length > 0) {
|
|
2569
|
-
const firstIdentifier = firstState.identifiers[0];
|
|
2570
|
-
return firstIdentifier?.identifier;
|
|
2571
|
-
}
|
|
2572
|
-
} catch (error) {
|
|
2573
|
-
console.error(
|
|
2574
|
-
`\u274C [XmtpResolver] Error resolving from inbox state:`,
|
|
2575
|
-
error
|
|
2576
|
-
);
|
|
2577
|
-
}
|
|
2578
|
-
return null;
|
|
2579
|
-
}
|
|
2580
|
-
/**
|
|
2581
|
-
* Get cached address if not expired
|
|
2582
|
-
*/
|
|
2583
|
-
getCachedAddress(inboxId) {
|
|
2584
|
-
const entry = this.addressCache.get(inboxId);
|
|
2585
|
-
if (!entry) return null;
|
|
2586
|
-
const now = Date.now();
|
|
2587
|
-
if (now - entry.timestamp > this.cacheTtl) {
|
|
2588
|
-
this.addressCache.delete(inboxId);
|
|
2589
|
-
return null;
|
|
2590
|
-
}
|
|
2591
|
-
return entry.address;
|
|
2592
|
-
}
|
|
2593
|
-
/**
|
|
2594
|
-
* Cache address with LRU eviction
|
|
2595
|
-
*/
|
|
2596
|
-
setCachedAddress(inboxId, address) {
|
|
2597
|
-
if (this.addressCache.size >= this.maxCacheSize) {
|
|
2598
|
-
const firstKey = this.addressCache.keys().next().value;
|
|
2599
|
-
if (firstKey) {
|
|
2600
|
-
this.addressCache.delete(firstKey);
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
this.addressCache.set(inboxId, {
|
|
2604
|
-
address,
|
|
2605
|
-
timestamp: Date.now()
|
|
2606
|
-
});
|
|
2607
|
-
}
|
|
2608
|
-
/**
|
|
2609
|
-
* Get cached message if not expired
|
|
2610
|
-
*/
|
|
2611
|
-
getCachedMessage(messageId) {
|
|
2612
|
-
const entry = this.messageCache.get(messageId);
|
|
2613
|
-
if (!entry) return void 0;
|
|
2614
|
-
const now = Date.now();
|
|
2615
|
-
if (now - entry.timestamp > this.messageCacheTtl) {
|
|
2616
|
-
this.messageCache.delete(messageId);
|
|
2617
|
-
return void 0;
|
|
2618
|
-
}
|
|
2619
|
-
return entry.message;
|
|
2620
|
-
}
|
|
2621
|
-
/**
|
|
2622
|
-
* Cache message with LRU eviction
|
|
2623
|
-
*/
|
|
2624
|
-
setCachedMessage(messageId, message) {
|
|
2625
|
-
if (this.messageCache.size >= this.maxMessageCacheSize) {
|
|
2626
|
-
const firstKey = this.messageCache.keys().next().value;
|
|
2627
|
-
if (firstKey) {
|
|
2628
|
-
this.messageCache.delete(firstKey);
|
|
2629
|
-
}
|
|
2630
|
-
}
|
|
2631
|
-
this.messageCache.set(messageId, {
|
|
2632
|
-
message,
|
|
2633
|
-
timestamp: Date.now()
|
|
2634
|
-
});
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* Pre-populate address cache from existing conversations
|
|
2638
|
-
*/
|
|
2639
|
-
async prePopulateCache() {
|
|
2640
|
-
console.log("\u{1F504} [XmtpResolver] Pre-populating address cache...");
|
|
2641
|
-
try {
|
|
2642
|
-
const conversations = await this.client.conversations.list();
|
|
2643
|
-
let cachedCount = 0;
|
|
2644
|
-
for (const conversation of conversations) {
|
|
2645
|
-
try {
|
|
2646
|
-
const members = await conversation.members();
|
|
2647
|
-
for (const member of members) {
|
|
2648
|
-
const ethIdentifier = member.accountIdentifiers.find(
|
|
2649
|
-
(id) => id.identifierKind === 0
|
|
2650
|
-
// IdentifierKind.Ethereum
|
|
2651
|
-
);
|
|
2652
|
-
if (ethIdentifier) {
|
|
2653
|
-
this.setCachedAddress(
|
|
2654
|
-
member.inboxId,
|
|
2655
|
-
ethIdentifier.identifier
|
|
2656
|
-
);
|
|
2657
|
-
cachedCount++;
|
|
2658
|
-
}
|
|
2659
|
-
}
|
|
2660
|
-
} catch (error) {
|
|
2661
|
-
console.error(
|
|
2662
|
-
"[XmtpResolver] Error pre-caching conversation members:",
|
|
2663
|
-
error
|
|
2664
|
-
);
|
|
2665
|
-
}
|
|
2666
|
-
}
|
|
2667
|
-
console.log(
|
|
2668
|
-
`\u2705 [XmtpResolver] Pre-cached ${cachedCount} address mappings`
|
|
2669
|
-
);
|
|
2670
|
-
} catch (error) {
|
|
2671
|
-
console.error("[XmtpResolver] Error pre-populating cache:", error);
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
/**
|
|
2675
|
-
* Clear all caches
|
|
2676
|
-
*/
|
|
2677
|
-
clearCache() {
|
|
2678
|
-
this.addressCache.clear();
|
|
2679
|
-
this.messageCache.clear();
|
|
2680
|
-
console.log("\u{1F5D1}\uFE0F [XmtpResolver] All caches cleared");
|
|
2681
|
-
}
|
|
2682
|
-
/**
|
|
2683
|
-
* Get cache statistics
|
|
2684
|
-
*/
|
|
2685
|
-
getCacheStats() {
|
|
2686
|
-
return {
|
|
2687
|
-
address: {
|
|
2688
|
-
size: this.addressCache.size,
|
|
2689
|
-
maxSize: this.maxCacheSize
|
|
2690
|
-
},
|
|
2691
|
-
message: {
|
|
2692
|
-
size: this.messageCache.size,
|
|
2693
|
-
maxSize: this.maxMessageCacheSize
|
|
2694
|
-
}
|
|
2695
|
-
};
|
|
2696
|
-
}
|
|
2697
|
-
};
|
|
2698
|
-
|
|
2699
|
-
// src/resolver/resolver.ts
|
|
2700
|
-
var Resolver = class {
|
|
2701
|
-
constructor(options) {
|
|
2702
|
-
__publicField(this, "addressResolver");
|
|
2703
|
-
__publicField(this, "ensResolver");
|
|
2704
|
-
__publicField(this, "basenameResolver");
|
|
2705
|
-
__publicField(this, "xmtpResolver");
|
|
2706
|
-
const resolverOptions = {
|
|
2707
|
-
maxCacheSize: options.maxCacheSize ?? 1e3,
|
|
2708
|
-
cacheTtl: options.cacheTtl ?? 36e5
|
|
2709
|
-
};
|
|
2710
|
-
this.addressResolver = new AddressResolver(
|
|
2711
|
-
options.xmtpClient,
|
|
2712
|
-
resolverOptions
|
|
2713
|
-
);
|
|
2714
|
-
this.xmtpResolver = new XmtpResolver(options.xmtpClient, resolverOptions);
|
|
2715
|
-
this.ensResolver = new ENSResolver({
|
|
2716
|
-
...resolverOptions,
|
|
2717
|
-
mainnetClient: options.mainnetClient
|
|
2718
|
-
});
|
|
2719
|
-
this.basenameResolver = new BasenameResolver({
|
|
2720
|
-
...resolverOptions,
|
|
2721
|
-
publicClient: options.baseClient
|
|
2722
|
-
});
|
|
2723
|
-
}
|
|
2724
|
-
// === Address Resolution Methods ===
|
|
2725
|
-
/**
|
|
2726
|
-
* Resolve user address from inbox ID with caching
|
|
2727
|
-
* Uses both AddressResolver and XmtpResolver for redundancy
|
|
2728
|
-
*/
|
|
2729
|
-
async resolveAddress(inboxId, conversationId) {
|
|
2730
|
-
let result = await this.addressResolver.resolveAddress(
|
|
2731
|
-
inboxId,
|
|
2732
|
-
conversationId
|
|
2733
|
-
);
|
|
2734
|
-
if (!result) {
|
|
2735
|
-
result = await this.xmtpResolver.resolveAddress(inboxId, conversationId);
|
|
2736
|
-
}
|
|
2737
|
-
return result;
|
|
2738
|
-
}
|
|
2739
|
-
// === ENS Resolution Methods ===
|
|
2740
|
-
/**
|
|
2741
|
-
* Resolve an ENS name to an Ethereum address
|
|
2742
|
-
*/
|
|
2743
|
-
async resolveENSName(ensName) {
|
|
2744
|
-
return this.ensResolver.resolveENSName(ensName);
|
|
2745
|
-
}
|
|
2746
|
-
/**
|
|
2747
|
-
* Resolve an address to its primary ENS name (reverse resolution)
|
|
2748
|
-
*/
|
|
2749
|
-
async resolveAddressToENS(address) {
|
|
2750
|
-
return this.ensResolver.resolveAddressToENS(address);
|
|
2751
|
-
}
|
|
2752
|
-
/**
|
|
2753
|
-
* Get ENS avatar for a given ENS name
|
|
2754
|
-
*/
|
|
2755
|
-
async getENSAvatar(ensName) {
|
|
2756
|
-
return this.ensResolver.getENSAvatar(ensName);
|
|
2757
|
-
}
|
|
2758
|
-
/**
|
|
2759
|
-
* Get ENS text record for a given ENS name and key
|
|
2760
|
-
*/
|
|
2761
|
-
async getENSTextRecord(ensName, key) {
|
|
2762
|
-
return this.ensResolver.getENSTextRecord(ensName, key);
|
|
2763
|
-
}
|
|
2764
|
-
/**
|
|
2765
|
-
* Get complete ENS profile for a given ENS name
|
|
2766
|
-
*/
|
|
2767
|
-
async getENSProfile(ensName) {
|
|
2768
|
-
return this.ensResolver.getENSProfile(ensName);
|
|
2769
|
-
}
|
|
2770
|
-
// === Basename Resolution Methods ===
|
|
2771
|
-
/**
|
|
2772
|
-
* Get basename from an Ethereum address
|
|
2773
|
-
*/
|
|
2774
|
-
async getBasename(address) {
|
|
2775
|
-
return this.basenameResolver.getBasename(address);
|
|
2776
|
-
}
|
|
2777
|
-
/**
|
|
2778
|
-
* Get basename avatar for a given basename
|
|
2779
|
-
*/
|
|
2780
|
-
async getBasenameAvatar(basename) {
|
|
2781
|
-
return this.basenameResolver.getBasenameAvatar(basename);
|
|
2782
|
-
}
|
|
2783
|
-
/**
|
|
2784
|
-
* Get basename text record for a given basename and key
|
|
2785
|
-
*/
|
|
2786
|
-
async getBasenameTextRecord(basename, key) {
|
|
2787
|
-
return this.basenameResolver.getBasenameTextRecord(basename, key);
|
|
2788
|
-
}
|
|
2789
|
-
/**
|
|
2790
|
-
* Resolve basename to an Ethereum address
|
|
2791
|
-
*/
|
|
2792
|
-
async getBasenameAddress(basename) {
|
|
2793
|
-
return this.basenameResolver.getBasenameAddress(basename);
|
|
2794
|
-
}
|
|
2795
|
-
/**
|
|
2796
|
-
* Get basename metadata for a given basename
|
|
2797
|
-
*/
|
|
2798
|
-
async getBasenameMetadata(basename) {
|
|
2799
|
-
return this.basenameResolver.getBasenameMetadata(basename);
|
|
2800
|
-
}
|
|
2801
|
-
/**
|
|
2802
|
-
* Get complete basename profile for a given address
|
|
2803
|
-
*/
|
|
2804
|
-
async resolveBasenameProfile(address) {
|
|
2805
|
-
return this.basenameResolver.resolveBasenameProfile(address);
|
|
2806
|
-
}
|
|
2807
|
-
// === XMTP Message Methods ===
|
|
2808
|
-
/**
|
|
2809
|
-
* Find any message by ID with caching
|
|
2810
|
-
*/
|
|
2811
|
-
async findMessage(messageId) {
|
|
2812
|
-
return this.xmtpResolver.findMessage(messageId);
|
|
2813
|
-
}
|
|
2814
|
-
/**
|
|
2815
|
-
* Find root message by ID (traverses reply chain)
|
|
2816
|
-
*/
|
|
2817
|
-
async findRootMessage(messageId) {
|
|
2818
|
-
return this.xmtpResolver.findRootMessage(messageId);
|
|
2819
|
-
}
|
|
2820
|
-
// === Universal Resolution Methods ===
|
|
2821
|
-
/**
|
|
2822
|
-
* Universal name resolution - tries to resolve any name (ENS or basename) to an address
|
|
2823
|
-
*/
|
|
2824
|
-
async resolveName(name) {
|
|
2825
|
-
if (name.endsWith(".eth")) {
|
|
2826
|
-
return this.resolveENSName(name);
|
|
2827
|
-
}
|
|
2828
|
-
if (name.endsWith(".base.eth")) {
|
|
2829
|
-
return this.getBasenameAddress(name);
|
|
2830
|
-
}
|
|
2831
|
-
const ensResult = await this.resolveENSName(name);
|
|
2832
|
-
if (ensResult) {
|
|
2833
|
-
return ensResult;
|
|
2834
|
-
}
|
|
2835
|
-
return this.getBasenameAddress(name);
|
|
2836
|
-
}
|
|
2837
|
-
/**
|
|
2838
|
-
* Universal reverse resolution - tries to resolve an address to any name (ENS or basename)
|
|
2839
|
-
*/
|
|
2840
|
-
async resolveAddressToName(address) {
|
|
2841
|
-
const basename = await this.getBasename(address);
|
|
2842
|
-
if (basename) {
|
|
2843
|
-
return basename;
|
|
2844
|
-
}
|
|
2845
|
-
return this.resolveAddressToENS(address);
|
|
2846
|
-
}
|
|
2847
|
-
/**
|
|
2848
|
-
* Get complete profile for an address (combines ENS and basename data)
|
|
2849
|
-
*/
|
|
2850
|
-
async getCompleteProfile(address) {
|
|
2851
|
-
const [ensName, basename, ensProfile, basenameProfile] = await Promise.allSettled([
|
|
2852
|
-
this.resolveAddressToENS(address),
|
|
2853
|
-
this.getBasename(address),
|
|
2854
|
-
this.resolveAddressToENS(address).then(
|
|
2855
|
-
(name) => name ? this.getENSProfile(name) : null
|
|
2856
|
-
),
|
|
2857
|
-
this.resolveBasenameProfile(address)
|
|
2858
|
-
]);
|
|
2859
|
-
return {
|
|
2860
|
-
address,
|
|
2861
|
-
ensName: ensName.status === "fulfilled" ? ensName.value : null,
|
|
2862
|
-
basename: basename.status === "fulfilled" ? basename.value : null,
|
|
2863
|
-
ensProfile: ensProfile.status === "fulfilled" ? ensProfile.value : null,
|
|
2864
|
-
basenameProfile: basenameProfile.status === "fulfilled" ? basenameProfile.value : null
|
|
2865
|
-
};
|
|
2866
|
-
}
|
|
2867
|
-
// === Cache Management Methods ===
|
|
2868
|
-
/**
|
|
2869
|
-
* Pre-populate all resolver caches
|
|
2870
|
-
*/
|
|
2871
|
-
async prePopulateAllCaches() {
|
|
2872
|
-
await Promise.allSettled([
|
|
2873
|
-
this.addressResolver.prePopulateCache(),
|
|
2874
|
-
this.xmtpResolver.prePopulateCache()
|
|
2875
|
-
]);
|
|
2876
|
-
}
|
|
2877
|
-
/**
|
|
2878
|
-
* Create a complete XmtpSender object from an address or inboxId
|
|
2879
|
-
* Uses the resolver to get the best available name and profile information
|
|
2880
|
-
*/
|
|
2881
|
-
async createXmtpSender(addressOrInboxId, conversationId) {
|
|
2882
|
-
let address = null;
|
|
2883
|
-
let inboxId = addressOrInboxId;
|
|
2884
|
-
if (addressOrInboxId.startsWith("0x") && addressOrInboxId.length === 42) {
|
|
2885
|
-
address = addressOrInboxId;
|
|
2886
|
-
inboxId = addressOrInboxId;
|
|
2887
|
-
} else {
|
|
2888
|
-
address = await this.resolveAddress(addressOrInboxId, conversationId);
|
|
2889
|
-
}
|
|
2890
|
-
let name = "Unknown";
|
|
2891
|
-
let basename;
|
|
2892
|
-
if (address) {
|
|
2893
|
-
const basenameResult = await this.getBasename(address);
|
|
2894
|
-
console.log(
|
|
2895
|
-
`\u{1F50D} [RESOLVER] Direct basename lookup for ${address}:`,
|
|
2896
|
-
basenameResult
|
|
2897
|
-
);
|
|
2898
|
-
const resolvedName = await this.resolveAddressToName(address);
|
|
2899
|
-
console.log(
|
|
2900
|
-
`\u{1F50D} [RESOLVER] Universal name resolution for ${address}:`,
|
|
2901
|
-
resolvedName
|
|
2902
|
-
);
|
|
2903
|
-
if (resolvedName) {
|
|
2904
|
-
name = resolvedName;
|
|
2905
|
-
if (resolvedName.endsWith(".base.eth")) {
|
|
2906
|
-
basename = resolvedName;
|
|
2907
|
-
}
|
|
2908
|
-
} else {
|
|
2909
|
-
name = `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
2910
|
-
}
|
|
2911
|
-
if (!basename) {
|
|
2912
|
-
const resolvedBasename = await this.getBasename(address);
|
|
2913
|
-
basename = resolvedBasename || void 0;
|
|
2914
|
-
}
|
|
2915
|
-
} else {
|
|
2916
|
-
name = `${inboxId.slice(0, 8)}...${inboxId.slice(-4)}`;
|
|
2917
|
-
}
|
|
2918
|
-
return {
|
|
2919
|
-
address: address || addressOrInboxId,
|
|
2920
|
-
inboxId,
|
|
2921
|
-
name,
|
|
2922
|
-
basename
|
|
2923
|
-
};
|
|
2924
|
-
}
|
|
2925
|
-
};
|
|
2926
|
-
|
|
2927
|
-
// src/lib/message-listener.ts
|
|
2928
|
-
var MessageListener = class extends import_node_events.EventEmitter {
|
|
2929
|
-
constructor(config) {
|
|
2930
|
-
super();
|
|
2931
|
-
__publicField(this, "xmtpClient");
|
|
2932
|
-
__publicField(this, "resolver");
|
|
2933
|
-
__publicField(this, "filter");
|
|
2934
|
-
__publicField(this, "heartbeatInterval");
|
|
2935
|
-
__publicField(this, "fallbackCheckInterval");
|
|
2936
|
-
__publicField(this, "messageCount", 0);
|
|
2937
|
-
__publicField(this, "conversations", []);
|
|
2938
|
-
__publicField(this, "config");
|
|
2939
|
-
this.xmtpClient = config.xmtpClient;
|
|
2940
|
-
const mainnetClient = (0, import_viem3.createPublicClient)({
|
|
2941
|
-
chain: import_chains3.mainnet,
|
|
2942
|
-
transport: (0, import_viem3.http)()
|
|
2943
|
-
});
|
|
2944
|
-
this.resolver = new Resolver({
|
|
2945
|
-
xmtpClient: this.xmtpClient,
|
|
2946
|
-
mainnetClient,
|
|
2947
|
-
baseClient: config.publicClient,
|
|
2948
|
-
maxCacheSize: 1e3,
|
|
2949
|
-
cacheTtl: 864e5
|
|
2950
|
-
// 24 hours
|
|
2951
|
-
});
|
|
2952
|
-
this.filter = config.filter;
|
|
2953
|
-
this.config = {
|
|
2954
|
-
heartbeatInterval: config.heartbeatInterval ?? 3e5,
|
|
2955
|
-
// 5 minutes
|
|
2956
|
-
conversationCheckInterval: config.conversationCheckInterval ?? 3e4,
|
|
2957
|
-
// 30 seconds
|
|
2958
|
-
envKey: config.envKey ?? "XMTP_ENV"
|
|
2959
|
-
};
|
|
2960
|
-
}
|
|
2961
|
-
// Type-safe event emitter methods
|
|
2962
|
-
on(event, listener) {
|
|
2963
|
-
return super.on(event, listener);
|
|
2964
|
-
}
|
|
2965
|
-
emit(event, ...args) {
|
|
2966
|
-
return super.emit(event, ...args);
|
|
2967
|
-
}
|
|
2968
|
-
async start() {
|
|
2969
|
-
const XMTP_ENV = process.env[this.config.envKey];
|
|
2970
|
-
await this.resolver?.prePopulateAllCaches();
|
|
2971
|
-
console.log("\u{1F4E1} Syncing conversations...");
|
|
2972
|
-
await this.xmtpClient.conversations.sync();
|
|
2973
|
-
const address = this.xmtpClient.accountIdentifier?.identifier;
|
|
2974
|
-
this.conversations = await this.xmtpClient.conversations.list();
|
|
2975
|
-
console.log(`\u{1F916} XMTP[${XMTP_ENV}] Listening on ${address} ...`);
|
|
2976
|
-
this.emit("started");
|
|
2977
|
-
try {
|
|
2978
|
-
const stream = await this.xmtpClient.conversations.streamAllMessages();
|
|
2979
|
-
this.heartbeatInterval = setInterval(() => {
|
|
2980
|
-
this.emit("heartbeat", {
|
|
2981
|
-
messageCount: this.messageCount,
|
|
2982
|
-
conversationCount: this.conversations.length
|
|
2983
|
-
});
|
|
2984
|
-
if (this.messageCount > 0) {
|
|
2985
|
-
console.log(`\u{1F493} Active - processed ${this.messageCount} messages`);
|
|
2986
|
-
}
|
|
2987
|
-
}, this.config.heartbeatInterval);
|
|
2988
|
-
this.fallbackCheckInterval = setInterval(async () => {
|
|
2989
|
-
try {
|
|
2990
|
-
const latestConversations = await this.xmtpClient.conversations.list();
|
|
2991
|
-
if (latestConversations.length > this.conversations.length) {
|
|
2992
|
-
console.log(
|
|
2993
|
-
`\u{1F195} Detected ${latestConversations.length - this.conversations.length} new conversations`
|
|
2994
|
-
);
|
|
2995
|
-
this.conversations.push(
|
|
2996
|
-
...latestConversations.slice(this.conversations.length)
|
|
2997
|
-
);
|
|
2998
|
-
}
|
|
2999
|
-
} catch (error) {
|
|
3000
|
-
console.error("\u274C Error checking for new conversations:", error);
|
|
3001
|
-
this.emit("error", error);
|
|
3002
|
-
}
|
|
3003
|
-
}, this.config.conversationCheckInterval);
|
|
3004
|
-
try {
|
|
3005
|
-
for await (const message of stream) {
|
|
3006
|
-
this.messageCount++;
|
|
3007
|
-
try {
|
|
3008
|
-
if (!message || message.senderInboxId.toLowerCase() === this.xmtpClient.inboxId.toLowerCase()) {
|
|
3009
|
-
continue;
|
|
3010
|
-
}
|
|
3011
|
-
console.log(
|
|
3012
|
-
`\u{1F4E8} Received message "${JSON.stringify(message)}" in ${message.conversationId}`
|
|
3013
|
-
);
|
|
3014
|
-
const conversation = await this.xmtpClient.conversations.getConversationById(
|
|
3015
|
-
message.conversationId
|
|
3016
|
-
);
|
|
3017
|
-
if (!conversation) {
|
|
3018
|
-
console.log("\u274C Could not find conversation for message");
|
|
3019
|
-
continue;
|
|
3020
|
-
}
|
|
3021
|
-
const contentTypeId = message.contentType?.typeId;
|
|
3022
|
-
let messageContent;
|
|
3023
|
-
if (contentTypeId === "reply") {
|
|
3024
|
-
const replyContent = message.content;
|
|
3025
|
-
messageContent = (replyContent?.content || "").toString();
|
|
3026
|
-
} else if (contentTypeId === "remoteStaticAttachment" || contentTypeId === "attachment") {
|
|
3027
|
-
messageContent = message.fallback || message.content?.filename || "[Attachment]";
|
|
3028
|
-
} else if (contentTypeId === "reaction") {
|
|
3029
|
-
const reactionContent = message.content;
|
|
3030
|
-
messageContent = `[Reaction: ${reactionContent.content || ""}]`;
|
|
3031
|
-
} else {
|
|
3032
|
-
messageContent = message.content ? String(message.content) : "";
|
|
3033
|
-
}
|
|
3034
|
-
let rootMessage = message;
|
|
3035
|
-
let parentMessage = null;
|
|
3036
|
-
if (contentTypeId === "reply") {
|
|
3037
|
-
const { reference } = message.content;
|
|
3038
|
-
rootMessage = await this.resolver.findRootMessage(reference);
|
|
3039
|
-
parentMessage = await this.resolver.findMessage(reference);
|
|
3040
|
-
} else if (contentTypeId === "reaction") {
|
|
3041
|
-
const { reference } = message.content;
|
|
3042
|
-
rootMessage = await this.resolver.findRootMessage(reference);
|
|
3043
|
-
parentMessage = await this.resolver.findMessage(reference);
|
|
3044
|
-
} else {
|
|
3045
|
-
rootMessage = message;
|
|
3046
|
-
parentMessage = null;
|
|
3047
|
-
}
|
|
3048
|
-
if (!rootMessage) {
|
|
3049
|
-
console.warn(
|
|
3050
|
-
`\u26A0\uFE0F [MessageListener] Could not find root message for: ${message.id}`
|
|
3051
|
-
);
|
|
3052
|
-
continue;
|
|
3053
|
-
}
|
|
3054
|
-
if (this.filter) {
|
|
3055
|
-
const shouldProcess = await this.filter({
|
|
3056
|
-
conversation,
|
|
3057
|
-
message,
|
|
3058
|
-
rootMessage
|
|
3059
|
-
});
|
|
3060
|
-
if (!shouldProcess) {
|
|
3061
|
-
console.log("\u{1F504} Skipping message:", message.id);
|
|
3062
|
-
continue;
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
const sender = await this.resolver.createXmtpSender(
|
|
3066
|
-
message.senderInboxId,
|
|
3067
|
-
message.conversationId
|
|
3068
|
-
);
|
|
3069
|
-
const subjects = {};
|
|
3070
|
-
const messageEvent = {
|
|
3071
|
-
conversation,
|
|
3072
|
-
message,
|
|
3073
|
-
rootMessage,
|
|
3074
|
-
// We already checked it's not null above
|
|
3075
|
-
parentMessage: parentMessage || void 0,
|
|
3076
|
-
sender,
|
|
3077
|
-
subjects
|
|
3078
|
-
};
|
|
3079
|
-
this.emit("message", messageEvent);
|
|
3080
|
-
} catch (messageError) {
|
|
3081
|
-
console.error("\u274C Error processing message:", messageError);
|
|
3082
|
-
this.emit("error", messageError);
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
} catch (streamError) {
|
|
3086
|
-
console.error("\u274C Error in message stream:", streamError);
|
|
3087
|
-
this.cleanup();
|
|
3088
|
-
this.emit("error", streamError);
|
|
3089
|
-
console.log("\u{1F504} Attempting to restart stream...");
|
|
3090
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
3091
|
-
return this.start();
|
|
3092
|
-
}
|
|
3093
|
-
} catch (streamSetupError) {
|
|
3094
|
-
console.error("\u274C Error setting up message stream:", streamSetupError);
|
|
3095
|
-
this.emit("error", streamSetupError);
|
|
3096
|
-
throw streamSetupError;
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
cleanup() {
|
|
3100
|
-
if (this.heartbeatInterval) {
|
|
3101
|
-
clearInterval(this.heartbeatInterval);
|
|
3102
|
-
}
|
|
3103
|
-
if (this.fallbackCheckInterval) {
|
|
3104
|
-
clearInterval(this.fallbackCheckInterval);
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
stop() {
|
|
3108
|
-
this.cleanup();
|
|
3109
|
-
this.emit("stopped");
|
|
3110
|
-
console.log("\u{1F6D1} Message listener stopped");
|
|
3111
|
-
this.removeAllListeners();
|
|
3112
|
-
}
|
|
3113
|
-
/**
|
|
3114
|
-
* Get current statistics
|
|
3115
|
-
*/
|
|
3116
|
-
getStats() {
|
|
3117
|
-
return {
|
|
3118
|
-
messageCount: this.messageCount,
|
|
3119
|
-
conversationCount: this.conversations.length,
|
|
3120
|
-
isActive: !!this.heartbeatInterval
|
|
3121
|
-
};
|
|
3122
|
-
}
|
|
3123
|
-
};
|
|
3124
|
-
async function startMessageListener(config) {
|
|
3125
|
-
const listener = new MessageListener(config);
|
|
3126
|
-
await listener.start();
|
|
3127
|
-
return listener;
|
|
3128
|
-
}
|
|
3129
|
-
function createMessageListener(config) {
|
|
3130
|
-
return new MessageListener(config);
|
|
3131
|
-
}
|
|
3132
|
-
|
|
3133
|
-
// src/lib/subjects.ts
|
|
3134
|
-
function extractMentionedNames(content) {
|
|
3135
|
-
const nameRegex = /@([a-zA-Z0-9-_]+\.(?:base\.)?eth)\b/gi;
|
|
3136
|
-
const matches = content.match(nameRegex);
|
|
3137
|
-
if (!matches) {
|
|
3138
|
-
return [];
|
|
3139
|
-
}
|
|
3140
|
-
const names = matches.map((match) => match.slice(1).toLowerCase());
|
|
3141
|
-
return [...new Set(names)];
|
|
3142
|
-
}
|
|
3143
|
-
async function resolveSubjects(mentionedNames, basenameResolver, ensResolver) {
|
|
3144
|
-
const subjects = {};
|
|
3145
|
-
if (mentionedNames.length === 0) {
|
|
3146
|
-
return subjects;
|
|
3147
|
-
}
|
|
3148
|
-
console.log(
|
|
3149
|
-
`\u{1F50D} Found ${mentionedNames.length} name mentions:`,
|
|
3150
|
-
mentionedNames
|
|
3151
|
-
);
|
|
3152
|
-
for (const mentionedName of mentionedNames) {
|
|
3153
|
-
try {
|
|
3154
|
-
let resolvedAddress = null;
|
|
3155
|
-
if (ensResolver.isENSName(mentionedName)) {
|
|
3156
|
-
console.log(`\u{1F50D} Resolving ENS name: ${mentionedName}`);
|
|
3157
|
-
resolvedAddress = await ensResolver.resolveENSName(mentionedName);
|
|
3158
|
-
} else {
|
|
3159
|
-
console.log(`\u{1F50D} Resolving basename: ${mentionedName}`);
|
|
3160
|
-
resolvedAddress = await basenameResolver.getBasenameAddress(mentionedName);
|
|
3161
|
-
}
|
|
3162
|
-
if (resolvedAddress) {
|
|
3163
|
-
subjects[mentionedName] = resolvedAddress;
|
|
3164
|
-
console.log(`\u2705 Resolved ${mentionedName} \u2192 ${resolvedAddress}`);
|
|
3165
|
-
} else {
|
|
3166
|
-
console.log(`\u274C Could not resolve address for: ${mentionedName}`);
|
|
3167
|
-
}
|
|
3168
|
-
} catch (error) {
|
|
3169
|
-
console.error(`\u274C Error resolving ${mentionedName}:`, error);
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
return subjects;
|
|
3173
|
-
}
|
|
3174
|
-
async function extractSubjects(content, basenameResolver, ensResolver) {
|
|
3175
|
-
const mentionedNames = extractMentionedNames(content);
|
|
3176
|
-
return await resolveSubjects(mentionedNames, basenameResolver, ensResolver);
|
|
3177
|
-
}
|
|
3178
|
-
|
|
3179
|
-
// src/service-client.ts
|
|
3180
|
-
var XmtpServiceClient = class {
|
|
3181
|
-
constructor(config) {
|
|
3182
|
-
__publicField(this, "config");
|
|
3183
|
-
this.config = config;
|
|
3184
|
-
}
|
|
3185
|
-
async request(endpoint, body, method = "POST") {
|
|
3186
|
-
try {
|
|
3187
|
-
const baseUrl = this.config.serviceUrl.replace(/\/+$/, "");
|
|
3188
|
-
const isXmtpToolsEndpoint = endpoint.startsWith("/xmtp-tools/");
|
|
3189
|
-
const url = `${baseUrl}${endpoint}?token=${this.config.serviceToken}`;
|
|
3190
|
-
const headers = {
|
|
3191
|
-
"Content-Type": "application/json"
|
|
3192
|
-
};
|
|
3193
|
-
if (isXmtpToolsEndpoint) {
|
|
3194
|
-
headers.Authorization = `Bearer ${this.config.serviceToken}`;
|
|
3195
|
-
}
|
|
3196
|
-
const fetchOptions = {
|
|
3197
|
-
method,
|
|
3198
|
-
headers
|
|
3199
|
-
};
|
|
3200
|
-
if (method === "POST" && body) {
|
|
3201
|
-
fetchOptions.body = JSON.stringify(body);
|
|
3202
|
-
}
|
|
3203
|
-
const response = await fetch(url, fetchOptions);
|
|
3204
|
-
if (!response.ok) {
|
|
3205
|
-
let errorMessage = `HTTP ${response.status}`;
|
|
3206
|
-
try {
|
|
3207
|
-
const responseText = await response.text();
|
|
3208
|
-
try {
|
|
3209
|
-
const errorData = JSON.parse(responseText);
|
|
3210
|
-
errorMessage = errorData.error || errorMessage;
|
|
3211
|
-
} catch {
|
|
3212
|
-
errorMessage = responseText || errorMessage;
|
|
3213
|
-
}
|
|
3214
|
-
} catch {
|
|
3215
|
-
}
|
|
3216
|
-
throw new Error(errorMessage);
|
|
3217
|
-
}
|
|
3218
|
-
return {
|
|
3219
|
-
success: true,
|
|
3220
|
-
data: await response.json()
|
|
3221
|
-
};
|
|
3222
|
-
} catch (error) {
|
|
3223
|
-
console.error(
|
|
3224
|
-
`\u274C [XmtpServiceClient] Request to ${endpoint} failed:`,
|
|
3225
|
-
error
|
|
3226
|
-
);
|
|
3227
|
-
return {
|
|
3228
|
-
success: false,
|
|
3229
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
3230
|
-
};
|
|
3231
|
-
}
|
|
3232
|
-
}
|
|
3233
|
-
async sendMessage(params) {
|
|
3234
|
-
return this.request("/xmtp-tools/send", {
|
|
3235
|
-
content: params.content
|
|
3236
|
-
});
|
|
3237
|
-
}
|
|
3238
|
-
async sendReply(params) {
|
|
3239
|
-
return this.request("/xmtp-tools/reply", {
|
|
3240
|
-
content: params.content,
|
|
3241
|
-
messageId: params.messageId
|
|
3242
|
-
});
|
|
3243
|
-
}
|
|
3244
|
-
async sendReaction(params) {
|
|
3245
|
-
return this.request("/xmtp-tools/react", {
|
|
3246
|
-
messageId: params.messageId,
|
|
3247
|
-
emoji: params.emoji,
|
|
3248
|
-
action: params.action
|
|
3249
|
-
});
|
|
3250
|
-
}
|
|
3251
|
-
async sendTransaction(params) {
|
|
3252
|
-
return this.request("/xmtp-tools/transaction", {
|
|
3253
|
-
fromAddress: params.fromAddress,
|
|
3254
|
-
chainId: params.chainId,
|
|
3255
|
-
calls: params.calls.map((call) => ({
|
|
3256
|
-
to: call.to,
|
|
3257
|
-
data: call.data,
|
|
3258
|
-
...call.gas && { gas: call.gas },
|
|
3259
|
-
value: call.value || "0x0",
|
|
3260
|
-
metadata: {
|
|
3261
|
-
...call.metadata,
|
|
3262
|
-
chainId: params.chainId,
|
|
3263
|
-
from: params.fromAddress,
|
|
3264
|
-
version: "1"
|
|
3265
|
-
}
|
|
3266
|
-
}))
|
|
3267
|
-
});
|
|
3268
|
-
}
|
|
3269
|
-
/**
|
|
3270
|
-
* Get a single message by ID
|
|
3271
|
-
*/
|
|
3272
|
-
async getMessage(params) {
|
|
3273
|
-
return this.request(
|
|
3274
|
-
`/xmtp-tools/messages/${params.messageId}`,
|
|
3275
|
-
void 0,
|
|
3276
|
-
"GET"
|
|
3277
|
-
);
|
|
3278
|
-
}
|
|
3279
|
-
// getConversationMessages removed - superseded by thread-based approach
|
|
3280
|
-
};
|
|
3281
|
-
function createXmtpServiceClient(serviceUrl, serviceToken) {
|
|
3282
|
-
if (!serviceUrl || !serviceToken) {
|
|
3283
|
-
throw new Error("Missing XMTP service URL or token from runtime context");
|
|
3284
|
-
}
|
|
3285
|
-
return new XmtpServiceClient({
|
|
3286
|
-
serviceUrl,
|
|
3287
|
-
serviceToken
|
|
3288
|
-
});
|
|
3289
|
-
}
|
|
3290
|
-
function getXmtpAuthConfig(callbackUrl, callbackToken) {
|
|
3291
|
-
if (callbackUrl && callbackToken) {
|
|
3292
|
-
console.log("\u{1F511} [XmtpAuth] Using callback-provided credentials");
|
|
3293
|
-
return {
|
|
3294
|
-
serviceUrl: callbackUrl,
|
|
3295
|
-
serviceToken: callbackToken,
|
|
3296
|
-
source: "callback"
|
|
3297
|
-
};
|
|
3298
|
-
}
|
|
3299
|
-
const envUrl = process.env.XMTP_HOST;
|
|
3300
|
-
const envToken = process.env.XMTP_API_KEY;
|
|
3301
|
-
if (envUrl && envToken) {
|
|
3302
|
-
console.log("\u{1F511} [XmtpAuth] Using environment credentials");
|
|
3303
|
-
return {
|
|
3304
|
-
serviceUrl: envUrl,
|
|
3305
|
-
serviceToken: envToken,
|
|
3306
|
-
source: "environment"
|
|
3307
|
-
};
|
|
3308
|
-
}
|
|
3309
|
-
console.error(
|
|
3310
|
-
"\u274C [XmtpAuth] No XMTP credentials found in callback or environment"
|
|
3311
|
-
);
|
|
3312
|
-
console.error(
|
|
3313
|
-
"\u{1F4A1} [XmtpAuth] Expected: XMTP_HOST + XMTP_API_KEY or callback credentials"
|
|
3314
|
-
);
|
|
3315
|
-
return null;
|
|
3316
|
-
}
|
|
3317
|
-
function createAuthenticatedXmtpClient(callbackUrl, callbackToken) {
|
|
3318
|
-
const authConfig = getXmtpAuthConfig(callbackUrl, callbackToken);
|
|
3319
|
-
if (!authConfig) {
|
|
3320
|
-
throw new Error("No XMTP credentials found");
|
|
3321
|
-
}
|
|
3322
|
-
console.log(
|
|
3323
|
-
`\u{1F517} [XmtpAuth] Creating XMTP client (${authConfig.source} credentials)`
|
|
3324
|
-
);
|
|
3325
|
-
return createXmtpServiceClient(authConfig.serviceUrl, authConfig.serviceToken);
|
|
3326
|
-
}
|
|
3327
|
-
function getXMTPToolsUrl(baseUrl, action, token) {
|
|
3328
|
-
return `${baseUrl}/xmtp-tools/${action}?token=${token}`;
|
|
3329
|
-
}
|
|
3330
|
-
|
|
3331
|
-
// src/endpoints.ts
|
|
3332
|
-
var import_content_type_reaction2 = require("@xmtp/content-type-reaction");
|
|
3333
|
-
var import_content_type_reply2 = require("@xmtp/content-type-reply");
|
|
3334
|
-
var import_content_type_text = require("@xmtp/content-type-text");
|
|
3335
|
-
var import_content_type_wallet_send_calls2 = require("@xmtp/content-type-wallet-send-calls");
|
|
3336
|
-
var import_hono = require("hono");
|
|
3337
|
-
|
|
3338
|
-
// src/lib/jwt.ts
|
|
3339
|
-
var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
|
|
3340
|
-
function getValidatedPayload(c) {
|
|
3341
|
-
const authHeader = c.req.header("Authorization");
|
|
3342
|
-
if (authHeader?.startsWith("Bearer ")) {
|
|
3343
|
-
const token2 = authHeader.substring(7);
|
|
3344
|
-
return validateXMTPToolsToken(token2);
|
|
3345
|
-
}
|
|
3346
|
-
const token = c.req.query("token");
|
|
3347
|
-
if (!token) {
|
|
3348
|
-
return null;
|
|
3349
|
-
}
|
|
3350
|
-
return validateXMTPToolsToken(token);
|
|
3351
|
-
}
|
|
3352
|
-
function getJwtSecret() {
|
|
3353
|
-
const secret = process.env.XMTP_ENCRYPTION_KEY;
|
|
3354
|
-
const nodeEnv = process.env.NODE_ENV || "development";
|
|
3355
|
-
if (nodeEnv === "production" && !secret) {
|
|
3356
|
-
throw new Error(
|
|
3357
|
-
"XMTP_ENCRYPTION_KEY environment variable is required in production. Generate a secure random secret for JWT token signing."
|
|
3358
|
-
);
|
|
3359
|
-
}
|
|
3360
|
-
if (!secret) {
|
|
3361
|
-
console.warn(
|
|
3362
|
-
"\u26A0\uFE0F [SECURITY] Using fallback JWT secret for development. Set XMTP_ENCRYPTION_KEY environment variable for production."
|
|
3363
|
-
);
|
|
3364
|
-
return "fallback-secret-for-dev-only";
|
|
3365
|
-
}
|
|
3366
|
-
return secret;
|
|
3367
|
-
}
|
|
3368
|
-
function getApiKey() {
|
|
3369
|
-
const apiKey = process.env.XMTP_API_KEY;
|
|
3370
|
-
const nodeEnv = process.env.NODE_ENV || "development";
|
|
3371
|
-
if (nodeEnv === "production" && !apiKey) {
|
|
3372
|
-
throw new Error(
|
|
3373
|
-
"XMTP_API_KEY environment variable is required in production. Generate a secure random API key for authentication."
|
|
3374
|
-
);
|
|
3375
|
-
}
|
|
3376
|
-
if (!apiKey) {
|
|
3377
|
-
console.warn(
|
|
3378
|
-
"\u26A0\uFE0F [SECURITY] Using fallback API key for development. Set XMTP_API_KEY environment variable for production."
|
|
3379
|
-
);
|
|
3380
|
-
return "fallback-api-key-for-dev-only";
|
|
3381
|
-
}
|
|
3382
|
-
return apiKey;
|
|
3383
|
-
}
|
|
3384
|
-
var JWT_EXPIRY = 5 * 60;
|
|
3385
|
-
function generateXMTPToolsToken(payload) {
|
|
3386
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
3387
|
-
const fullPayload = {
|
|
3388
|
-
...payload,
|
|
3389
|
-
issued: now,
|
|
3390
|
-
expires: now + JWT_EXPIRY
|
|
3391
|
-
};
|
|
3392
|
-
return import_jsonwebtoken.default.sign(fullPayload, getJwtSecret(), {
|
|
3393
|
-
expiresIn: JWT_EXPIRY
|
|
3394
|
-
});
|
|
3395
|
-
}
|
|
3396
|
-
function validateXMTPToolsToken(token) {
|
|
3397
|
-
if (token === getApiKey()) {
|
|
3398
|
-
console.log("\u{1F511} [Auth] Using API key authentication");
|
|
3399
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
3400
|
-
return {
|
|
3401
|
-
action: "send",
|
|
3402
|
-
// Default action
|
|
3403
|
-
conversationId: "",
|
|
3404
|
-
// Will be filled by endpoint
|
|
3405
|
-
issued: now,
|
|
3406
|
-
expires: now + 3600
|
|
3407
|
-
// API keys are valid for 1 hour
|
|
3408
|
-
};
|
|
3409
|
-
}
|
|
3410
|
-
try {
|
|
3411
|
-
const decoded = import_jsonwebtoken.default.verify(token, getJwtSecret());
|
|
3412
|
-
console.log("\u{1F511} [Auth] Using JWT token authentication");
|
|
3413
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
3414
|
-
if (decoded.expires < now) {
|
|
3415
|
-
console.warn("\u{1F512} XMTP tools token has expired");
|
|
3416
|
-
return null;
|
|
3417
|
-
}
|
|
3418
|
-
return decoded;
|
|
3419
|
-
} catch (error) {
|
|
3420
|
-
console.error(
|
|
3421
|
-
"\u{1F512} Invalid XMTP tools token and not matching API key:",
|
|
3422
|
-
error
|
|
3423
|
-
);
|
|
3424
|
-
return null;
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
// src/endpoints.ts
|
|
3429
|
-
var app = new import_hono.Hono();
|
|
3430
|
-
app.get("/messages/:messageId", async (c) => {
|
|
3431
|
-
const xmtpClient = c.get("xmtpClient");
|
|
3432
|
-
if (!xmtpClient) {
|
|
3433
|
-
return c.json({ error: "XMTP client not initialized" }, 500);
|
|
3434
|
-
}
|
|
3435
|
-
const token = c.req.query("token");
|
|
3436
|
-
if (!token) {
|
|
3437
|
-
return c.json({ error: "Token required" }, 401);
|
|
3438
|
-
}
|
|
3439
|
-
const payload = validateXMTPToolsToken(token);
|
|
3440
|
-
if (!payload) {
|
|
3441
|
-
return c.json({ error: "Invalid or expired token" }, 401);
|
|
3442
|
-
}
|
|
3443
|
-
const messageId = c.req.param("messageId");
|
|
3444
|
-
try {
|
|
3445
|
-
const message = await xmtpClient.conversations.getMessageById(messageId);
|
|
3446
|
-
if (!message) {
|
|
3447
|
-
return c.json({ error: "Message not found" }, 404);
|
|
3448
|
-
}
|
|
3449
|
-
console.log(`\u2705 Retrieved message ${messageId}`);
|
|
3450
|
-
const transformedMessage = {
|
|
3451
|
-
id: message.id,
|
|
3452
|
-
senderInboxId: message.senderInboxId,
|
|
3453
|
-
sentAt: message.sentAt.toISOString(),
|
|
3454
|
-
content: typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
3455
|
-
contentType: message.contentType?.typeId || "text",
|
|
3456
|
-
conversationId: message.conversationId,
|
|
3457
|
-
parameters: message.contentType?.parameters || {}
|
|
3458
|
-
};
|
|
3459
|
-
return c.json(transformedMessage);
|
|
3460
|
-
} catch (error) {
|
|
3461
|
-
console.error("\u274C Error fetching message:", error);
|
|
3462
|
-
return c.json({ error: "Failed to fetch message" }, 500);
|
|
3463
|
-
}
|
|
3464
|
-
});
|
|
3465
|
-
app.post("/send", async (c) => {
|
|
3466
|
-
const xmtpClient = c.get("xmtpClient");
|
|
3467
|
-
if (!xmtpClient) {
|
|
3468
|
-
return c.json({ error: "XMTP client not initialized" }, 500);
|
|
3469
|
-
}
|
|
3470
|
-
const payload = getValidatedPayload(c);
|
|
3471
|
-
if (!payload) {
|
|
3472
|
-
return c.json({ error: "Invalid or expired token" }, 401);
|
|
3473
|
-
}
|
|
3474
|
-
const body = await c.req.json();
|
|
3475
|
-
const content = body.content || payload.content;
|
|
3476
|
-
if (!content) {
|
|
3477
|
-
return c.json({ error: "Content required for send action" }, 400);
|
|
3478
|
-
}
|
|
3479
|
-
const conversationId = payload.conversationId;
|
|
3480
|
-
if (!conversationId) {
|
|
3481
|
-
return c.json({ error: "Conversation ID required" }, 400);
|
|
3482
|
-
}
|
|
3483
|
-
try {
|
|
3484
|
-
const conversation = await xmtpClient.conversations.getConversationById(conversationId);
|
|
3485
|
-
if (!conversation) {
|
|
3486
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
3487
|
-
}
|
|
3488
|
-
await conversation.send(content);
|
|
3489
|
-
console.log(`\u27A1 Sent message to conversation ${conversationId}`);
|
|
3490
|
-
return c.json({
|
|
3491
|
-
success: true,
|
|
3492
|
-
action: "send",
|
|
3493
|
-
conversationId: payload.conversationId
|
|
3494
|
-
});
|
|
3495
|
-
} catch (error) {
|
|
3496
|
-
console.error("\u274C Error sending message:", error);
|
|
3497
|
-
return c.json({ error: "Failed to send message" }, 500);
|
|
3498
|
-
}
|
|
3499
|
-
});
|
|
3500
|
-
app.post("/reply", async (c) => {
|
|
3501
|
-
const xmtpClient = c.get("xmtpClient");
|
|
3502
|
-
if (!xmtpClient) {
|
|
3503
|
-
return c.json({ error: "XMTP client not initialized" }, 500);
|
|
3504
|
-
}
|
|
3505
|
-
const payload = getValidatedPayload(c);
|
|
3506
|
-
if (!payload) {
|
|
3507
|
-
return c.json({ error: "Invalid or expired token" }, 401);
|
|
3508
|
-
}
|
|
3509
|
-
const body = await c.req.json();
|
|
3510
|
-
const content = body.content || payload.content;
|
|
3511
|
-
if (!content) {
|
|
3512
|
-
return c.json({ error: "Content required for reply action" }, 400);
|
|
3513
|
-
}
|
|
3514
|
-
const messageId = body.messageId;
|
|
3515
|
-
if (!messageId) {
|
|
3516
|
-
return c.json(
|
|
3517
|
-
{ error: "Reference message ID required for reply action" },
|
|
3518
|
-
400
|
|
3519
|
-
);
|
|
3520
|
-
}
|
|
3521
|
-
try {
|
|
3522
|
-
const conversation = await xmtpClient.conversations.getConversationById(
|
|
3523
|
-
payload.conversationId
|
|
3524
|
-
);
|
|
3525
|
-
if (!conversation) {
|
|
3526
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
3527
|
-
}
|
|
3528
|
-
const reply = {
|
|
3529
|
-
reference: messageId,
|
|
3530
|
-
contentType: import_content_type_text.ContentTypeText,
|
|
3531
|
-
content
|
|
3532
|
-
};
|
|
3533
|
-
await conversation.send(reply, import_content_type_reply2.ContentTypeReply);
|
|
3534
|
-
console.log(
|
|
3535
|
-
`\u27A1 Sent reply "${content}" to conversation ${payload.conversationId}`
|
|
3536
|
-
);
|
|
3537
|
-
return c.json({
|
|
3538
|
-
success: true,
|
|
3539
|
-
action: "reply",
|
|
3540
|
-
conversationId: payload.conversationId
|
|
3541
|
-
});
|
|
3542
|
-
} catch (error) {
|
|
3543
|
-
console.error("\u274C Error sending reply:", error);
|
|
3544
|
-
return c.json({ error: "Failed to send reply" }, 500);
|
|
3545
|
-
}
|
|
3546
|
-
});
|
|
3547
|
-
app.post("/react", async (c) => {
|
|
3548
|
-
const xmtpClient = c.get("xmtpClient");
|
|
3549
|
-
if (!xmtpClient) {
|
|
3550
|
-
return c.json({ error: "XMTP client not initialized" }, 500);
|
|
3551
|
-
}
|
|
3552
|
-
const payload = getValidatedPayload(c);
|
|
3553
|
-
if (!payload) {
|
|
3554
|
-
return c.json({ error: "Invalid or expired token" }, 401);
|
|
3555
|
-
}
|
|
3556
|
-
const body = await c.req.json();
|
|
3557
|
-
if (!body.emoji) {
|
|
3558
|
-
return c.json({ error: "Emoji required for react action" }, 400);
|
|
3559
|
-
}
|
|
3560
|
-
try {
|
|
3561
|
-
const conversation = await xmtpClient.conversations.getConversationById(
|
|
3562
|
-
payload.conversationId
|
|
3563
|
-
);
|
|
3564
|
-
if (!conversation) {
|
|
3565
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
3566
|
-
}
|
|
3567
|
-
const reaction = {
|
|
3568
|
-
schema: "unicode",
|
|
3569
|
-
reference: body.messageId,
|
|
3570
|
-
action: body.action,
|
|
3571
|
-
contentType: import_content_type_reaction2.ContentTypeReaction,
|
|
3572
|
-
content: body.emoji
|
|
3573
|
-
};
|
|
3574
|
-
await conversation.send(reaction, import_content_type_reaction2.ContentTypeReaction);
|
|
3575
|
-
console.log(
|
|
3576
|
-
`\u27A1 Sent reaction ${body.emoji} to message ${body.messageId} in conversation ${payload.conversationId}`
|
|
3577
|
-
);
|
|
3578
|
-
return c.json({
|
|
3579
|
-
success: true,
|
|
3580
|
-
action: "react",
|
|
3581
|
-
conversationId: payload.conversationId
|
|
3582
|
-
});
|
|
3583
|
-
} catch (error) {
|
|
3584
|
-
console.error("\u274C Error sending reaction:", error);
|
|
3585
|
-
return c.json({ error: "Failed to send reaction" }, 500);
|
|
3586
|
-
}
|
|
3587
|
-
});
|
|
3588
|
-
app.post("/transaction", async (c) => {
|
|
3589
|
-
const xmtpClient = c.get("xmtpClient");
|
|
3590
|
-
if (!xmtpClient) {
|
|
3591
|
-
return c.json({ error: "XMTP client not initialized" }, 500);
|
|
3592
|
-
}
|
|
3593
|
-
const payload = getValidatedPayload(c);
|
|
3594
|
-
if (!payload) {
|
|
3595
|
-
return c.json({ error: "Invalid or expired token" }, 401);
|
|
3596
|
-
}
|
|
3597
|
-
let body = {};
|
|
3598
|
-
try {
|
|
3599
|
-
body = await c.req.json();
|
|
3600
|
-
} catch (error) {
|
|
3601
|
-
body = {};
|
|
3602
|
-
}
|
|
3603
|
-
const fromAddress = payload.fromAddress || body.fromAddress;
|
|
3604
|
-
const chainId = payload.chainId || body.chainId;
|
|
3605
|
-
const calls = payload.calls || body.calls;
|
|
3606
|
-
if (!calls || !fromAddress || !chainId) {
|
|
3607
|
-
return c.json(
|
|
3608
|
-
{ error: "Transaction data required for transaction action" },
|
|
3609
|
-
400
|
|
3610
|
-
);
|
|
3611
|
-
}
|
|
3612
|
-
calls.forEach((call, index) => {
|
|
3613
|
-
if (call.data && typeof call.data === "string") {
|
|
3614
|
-
const actualStart = call.data.substring(0, 10);
|
|
3615
|
-
if (actualStart === "0x010f2e2e") {
|
|
3616
|
-
console.error("\u{1F6A8} CRITICAL: Transaction data truncation detected!");
|
|
3617
|
-
console.error(" Function selector corrupted - transaction will fail");
|
|
3618
|
-
console.error(
|
|
3619
|
-
" This indicates a bug in wallet software or XMTP transmission"
|
|
3620
|
-
);
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
});
|
|
3624
|
-
try {
|
|
3625
|
-
const conversation = await xmtpClient.conversations.getConversationById(
|
|
3626
|
-
payload.conversationId
|
|
3627
|
-
);
|
|
3628
|
-
if (!conversation) {
|
|
3629
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
3630
|
-
}
|
|
3631
|
-
const params = {
|
|
3632
|
-
version: "1",
|
|
3633
|
-
chainId,
|
|
3634
|
-
from: fromAddress,
|
|
3635
|
-
calls
|
|
3636
|
-
};
|
|
3637
|
-
await conversation.send(params, import_content_type_wallet_send_calls2.ContentTypeWalletSendCalls);
|
|
3638
|
-
console.log(
|
|
3639
|
-
`\u2705 Sent transaction request to conversation ${payload.conversationId}`
|
|
3640
|
-
);
|
|
3641
|
-
return c.json({
|
|
3642
|
-
success: true,
|
|
3643
|
-
action: "transaction",
|
|
3644
|
-
conversationId: payload.conversationId
|
|
3645
|
-
});
|
|
3646
|
-
} catch (error) {
|
|
3647
|
-
console.error("\u274C Error sending transaction:", error);
|
|
3648
|
-
return c.json({ error: "Failed to send transaction" }, 500);
|
|
3649
|
-
}
|
|
3650
|
-
});
|
|
3651
|
-
var endpoints_default = app;
|
|
3652
|
-
|
|
3653
|
-
// src/plugin.ts
|
|
3654
|
-
function XMTPPlugin({
|
|
3655
|
-
filter
|
|
3656
|
-
} = {}) {
|
|
3657
|
-
return {
|
|
3658
|
-
name: "xmtp",
|
|
3659
|
-
description: "Provides XMTP messaging functionality",
|
|
3660
|
-
apply: (app2, context) => {
|
|
3661
|
-
app2.route("/xmtp-tools", endpoints_default);
|
|
3662
|
-
}
|
|
3663
|
-
};
|
|
998
|
+
var JWT_EXPIRY = 5 * 60;
|
|
999
|
+
function generateXMTPToolsToken(payload) {
|
|
1000
|
+
const startTime = performance.now();
|
|
1001
|
+
import_utils3.logger.debug("\u{1F510} [JWT] Starting token generation...");
|
|
1002
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1003
|
+
const fullPayload = {
|
|
1004
|
+
...payload,
|
|
1005
|
+
issued: now,
|
|
1006
|
+
expires: now + JWT_EXPIRY
|
|
1007
|
+
};
|
|
1008
|
+
const token = import_jsonwebtoken.default.sign(fullPayload, getJwtSecret(), {
|
|
1009
|
+
expiresIn: JWT_EXPIRY
|
|
1010
|
+
});
|
|
1011
|
+
const endTime = performance.now();
|
|
1012
|
+
import_utils3.logger.debug(
|
|
1013
|
+
`\u{1F510} [JWT] Token generation completed in ${(endTime - startTime).toFixed(2)}ms`
|
|
1014
|
+
);
|
|
1015
|
+
return token;
|
|
3664
1016
|
}
|
|
3665
1017
|
|
|
3666
1018
|
// src/index.ts
|
|
3667
|
-
var
|
|
1019
|
+
var import_node_sdk3 = require("@xmtp/node-sdk");
|
|
3668
1020
|
var import_content_type_transaction_reference2 = require("@xmtp/content-type-transaction-reference");
|
|
3669
|
-
var
|
|
3670
|
-
var
|
|
3671
|
-
var
|
|
1021
|
+
var import_content_type_text = require("@xmtp/content-type-text");
|
|
1022
|
+
var import_content_type_reaction2 = require("@xmtp/content-type-reaction");
|
|
1023
|
+
var import_content_type_reply2 = require("@xmtp/content-type-reply");
|
|
3672
1024
|
var import_content_type_group_updated = require("@xmtp/content-type-group-updated");
|
|
3673
|
-
var
|
|
1025
|
+
var import_content_type_wallet_send_calls2 = require("@xmtp/content-type-wallet-send-calls");
|
|
3674
1026
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3675
1027
|
0 && (module.exports = {
|
|
3676
|
-
|
|
3677
|
-
BasenameTextRecordKeys,
|
|
1028
|
+
Agent,
|
|
3678
1029
|
Client,
|
|
3679
1030
|
ContentTypeGroupUpdated,
|
|
3680
1031
|
ContentTypeReaction,
|
|
@@ -3684,41 +1035,20 @@ var import_content_type_wallet_send_calls3 = require("@xmtp/content-type-wallet-
|
|
|
3684
1035
|
ContentTypeWalletSendCalls,
|
|
3685
1036
|
DEFAULT_AMOUNT,
|
|
3686
1037
|
DEFAULT_OPTIONS,
|
|
3687
|
-
ENSResolver,
|
|
3688
1038
|
GroupUpdatedCodec,
|
|
3689
1039
|
IdentifierKind,
|
|
3690
1040
|
MAX_USDC_AMOUNT,
|
|
3691
|
-
MessageListener,
|
|
3692
1041
|
ReplyCodec,
|
|
3693
|
-
Resolver,
|
|
3694
1042
|
XMTPConnectionManager,
|
|
3695
1043
|
XMTPPlugin,
|
|
3696
|
-
XmtpResolver,
|
|
3697
|
-
XmtpServiceClient,
|
|
3698
|
-
backupDbToPersistentStorage,
|
|
3699
|
-
convertChainIdToCoinType,
|
|
3700
|
-
convertReverseNodeToBytes,
|
|
3701
|
-
createAuthenticatedXmtpClient,
|
|
3702
|
-
createMessageListener,
|
|
3703
1044
|
createSigner,
|
|
3704
1045
|
createUser,
|
|
3705
1046
|
createXMTPClient,
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
diagnoseXMTPIdentityIssue,
|
|
3709
|
-
extractMentionedNames,
|
|
3710
|
-
extractSubjects,
|
|
3711
|
-
generateEncryptionKeyHex,
|
|
1047
|
+
createXMTPSigner,
|
|
1048
|
+
filter,
|
|
3712
1049
|
generateXMTPToolsToken,
|
|
3713
|
-
|
|
3714
|
-
getEncryptionKeyFromHex,
|
|
3715
|
-
getXMTPToolsUrl,
|
|
3716
|
-
getXmtpAuthConfig,
|
|
1050
|
+
getTestUrl,
|
|
3717
1051
|
logAgentDetails,
|
|
3718
|
-
resolveSubjects,
|
|
3719
|
-
resolveUserAddress,
|
|
3720
|
-
startMessageListener,
|
|
3721
|
-
startPeriodicBackup,
|
|
3722
1052
|
validateEnvironment
|
|
3723
1053
|
});
|
|
3724
1054
|
//# sourceMappingURL=index.cjs.map
|