@hybrd/xmtp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache/tsbuildinfo.json +1 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/README.md +380 -0
- package/biome.jsonc +4 -0
- package/package.json +46 -0
- package/scripts/generate-keys.ts +25 -0
- package/scripts/refresh-identity.ts +119 -0
- package/scripts/register-wallet.ts +95 -0
- package/scripts/revoke-all-installations.ts +91 -0
- package/scripts/revoke-installations.ts +94 -0
- package/src/abi/l2_resolver.ts +699 -0
- package/src/client.ts +940 -0
- package/src/constants.ts +6 -0
- package/src/index.ts +129 -0
- package/src/lib/message-listener.test.ts +369 -0
- package/src/lib/message-listener.ts +343 -0
- package/src/lib/subjects.ts +89 -0
- package/src/localStorage.ts.old +203 -0
- package/src/resolver/address-resolver.ts +221 -0
- package/src/resolver/basename-resolver.ts +585 -0
- package/src/resolver/ens-resolver.ts +324 -0
- package/src/resolver/index.ts +1 -0
- package/src/resolver/resolver.ts +336 -0
- package/src/resolver/xmtp-resolver.ts +436 -0
- package/src/service-client.ts +286 -0
- package/src/transactionMonitor.ts.old +275 -0
- package/src/types.ts +157 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type { XmtpClient } from "../types"
|
|
2
|
+
|
|
3
|
+
interface AddressResolverOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Maximum number of addresses to cache
|
|
6
|
+
* @default 1000
|
|
7
|
+
*/
|
|
8
|
+
maxCacheSize?: number
|
|
9
|
+
/**
|
|
10
|
+
* Cache TTL in milliseconds
|
|
11
|
+
* @default 86400000 (24 hours)
|
|
12
|
+
*/
|
|
13
|
+
cacheTtl?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface CacheEntry {
|
|
17
|
+
address: string
|
|
18
|
+
timestamp: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class AddressResolver {
|
|
22
|
+
private cache = new Map<string, CacheEntry>()
|
|
23
|
+
private readonly maxCacheSize: number
|
|
24
|
+
private readonly cacheTtl: number
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private client: XmtpClient,
|
|
28
|
+
options: AddressResolverOptions = {}
|
|
29
|
+
) {
|
|
30
|
+
this.maxCacheSize = options.maxCacheSize ?? 1000
|
|
31
|
+
this.cacheTtl = options.cacheTtl ?? 86400000 // 24 hours
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve user address from inbox ID with caching
|
|
36
|
+
*/
|
|
37
|
+
async resolveAddress(
|
|
38
|
+
inboxId: string,
|
|
39
|
+
conversationId?: string
|
|
40
|
+
): Promise<`0x${string}` | null> {
|
|
41
|
+
// Check cache first (fastest)
|
|
42
|
+
const cached = this.getCachedAddress(inboxId)
|
|
43
|
+
if (cached) {
|
|
44
|
+
console.log(`✅ Resolved user address from cache: ${cached}`)
|
|
45
|
+
return cached
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let userAddress = undefined
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Try conversation members lookup first (faster than network call)
|
|
52
|
+
if (conversationId) {
|
|
53
|
+
const conversation =
|
|
54
|
+
await this.client.conversations.getConversationById(conversationId)
|
|
55
|
+
if (conversation) {
|
|
56
|
+
userAddress = await this.resolveFromConversation(
|
|
57
|
+
conversation,
|
|
58
|
+
inboxId
|
|
59
|
+
)
|
|
60
|
+
if (userAddress) {
|
|
61
|
+
this.setCachedAddress(inboxId, userAddress)
|
|
62
|
+
console.log(`✅ Resolved user address: ${userAddress}`)
|
|
63
|
+
return userAddress
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Fallback to inboxStateFromInboxIds
|
|
69
|
+
userAddress = await this.resolveFromInboxState(inboxId)
|
|
70
|
+
if (userAddress) {
|
|
71
|
+
this.setCachedAddress(inboxId, userAddress)
|
|
72
|
+
console.log(`✅ Resolved user address via fallback: ${userAddress}`)
|
|
73
|
+
return userAddress
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`⚠️ No identifiers found for inbox ${inboxId}`)
|
|
77
|
+
return null
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`❌ Error resolving user address for ${inboxId}:`, error)
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve address from conversation members
|
|
86
|
+
*/
|
|
87
|
+
private async resolveFromConversation(
|
|
88
|
+
conversation: any,
|
|
89
|
+
inboxId: string
|
|
90
|
+
): Promise<`0x${string}` | null> {
|
|
91
|
+
try {
|
|
92
|
+
const members = await conversation.members()
|
|
93
|
+
const sender = members.find(
|
|
94
|
+
(member: any) => member.inboxId.toLowerCase() === inboxId.toLowerCase()
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if (sender) {
|
|
98
|
+
const ethIdentifier = sender.accountIdentifiers.find(
|
|
99
|
+
(id: any) => id.identifierKind === 0 // IdentifierKind.Ethereum
|
|
100
|
+
)
|
|
101
|
+
if (ethIdentifier) {
|
|
102
|
+
return ethIdentifier.identifier
|
|
103
|
+
} else {
|
|
104
|
+
console.log(`⚠️ No Ethereum identifier found for inbox ${inboxId}`)
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
console.log(
|
|
108
|
+
`⚠️ Sender not found in conversation members for inbox ${inboxId}`
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error(`❌ Error resolving from conversation members:`, error)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resolve address from inbox state (network fallback)
|
|
120
|
+
*/
|
|
121
|
+
private async resolveFromInboxState(inboxId: string): Promise<`0x${string}` | null> {
|
|
122
|
+
try {
|
|
123
|
+
const inboxState = await this.client.preferences.inboxStateFromInboxIds([
|
|
124
|
+
inboxId
|
|
125
|
+
])
|
|
126
|
+
const firstState = inboxState?.[0]
|
|
127
|
+
if (firstState?.identifiers && firstState.identifiers.length > 0) {
|
|
128
|
+
const firstIdentifier = firstState.identifiers[0]
|
|
129
|
+
return firstIdentifier?.identifier as `0x${string}`
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(`❌ Error resolving from inbox state:`, error)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get cached address if not expired
|
|
140
|
+
*/
|
|
141
|
+
private getCachedAddress(inboxId: string): `0x${string}` | null {
|
|
142
|
+
const entry = this.cache.get(inboxId)
|
|
143
|
+
if (!entry) return null
|
|
144
|
+
|
|
145
|
+
const now = Date.now()
|
|
146
|
+
if (now - entry.timestamp > this.cacheTtl) {
|
|
147
|
+
this.cache.delete(inboxId)
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return entry.address as `0x${string}`
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cache address with LRU eviction
|
|
156
|
+
*/
|
|
157
|
+
private setCachedAddress(inboxId: string, address: `0x${string}`): void {
|
|
158
|
+
// Simple LRU: if cache is full, remove oldest entry
|
|
159
|
+
if (this.cache.size >= this.maxCacheSize) {
|
|
160
|
+
const firstKey = this.cache.keys().next().value
|
|
161
|
+
if (firstKey) {
|
|
162
|
+
this.cache.delete(firstKey)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.cache.set(inboxId, {
|
|
167
|
+
address,
|
|
168
|
+
timestamp: Date.now()
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Pre-populate cache from existing conversations
|
|
174
|
+
*/
|
|
175
|
+
async prePopulateCache(): Promise<void> {
|
|
176
|
+
console.log("🔄 Pre-populating address cache...")
|
|
177
|
+
try {
|
|
178
|
+
const conversations = await this.client.conversations.list()
|
|
179
|
+
let cachedCount = 0
|
|
180
|
+
|
|
181
|
+
for (const conversation of conversations) {
|
|
182
|
+
try {
|
|
183
|
+
const members = await conversation.members()
|
|
184
|
+
for (const member of members) {
|
|
185
|
+
const ethIdentifier = member.accountIdentifiers.find(
|
|
186
|
+
(id: any) => id.identifierKind === 0 // IdentifierKind.Ethereum
|
|
187
|
+
)
|
|
188
|
+
if (ethIdentifier) {
|
|
189
|
+
this.setCachedAddress(member.inboxId, ethIdentifier.identifier as `0x${string}`)
|
|
190
|
+
cachedCount++
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error("Error pre-caching conversation members:", error)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`✅ Pre-cached ${cachedCount} address mappings`)
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error("Error pre-populating cache:", error)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Clear the cache
|
|
206
|
+
*/
|
|
207
|
+
clearCache(): void {
|
|
208
|
+
this.cache.clear()
|
|
209
|
+
console.log("🗑️ Address cache cleared")
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get cache statistics
|
|
214
|
+
*/
|
|
215
|
+
getCacheStats(): { size: number; maxSize: number; hitRate?: number } {
|
|
216
|
+
return {
|
|
217
|
+
size: this.cache.size,
|
|
218
|
+
maxSize: this.maxCacheSize
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|