@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
package/README.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Enhanced XMTP SDK with Robust Reliability π
|
|
2
|
+
|
|
3
|
+
This package provides an enhanced XMTP client with robust connection management, retry logic, and health monitoring capabilities.
|
|
4
|
+
|
|
5
|
+
## π Upgraded Features
|
|
6
|
+
|
|
7
|
+
- **XMTP Node SDK**: Upgraded to `^3.1.0` (latest version)
|
|
8
|
+
- **Enhanced Connection Management**: Automatic reconnection and health monitoring
|
|
9
|
+
- **Robust Retry Logic**: Exponential backoff and configurable retry strategies
|
|
10
|
+
- **Health Monitoring**: Real-time connection health tracking with metrics
|
|
11
|
+
- **Production Ready**: Built for scalable, reliable XMTP integrations
|
|
12
|
+
|
|
13
|
+
## ποΈ Architecture Overview
|
|
14
|
+
|
|
15
|
+
Your system uses a **"Thin Listener + QStash Callbacks"** architecture that already provides excellent reliability:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
βββββββββββββββ βββββββββββββββ βββββββββββββββ
|
|
19
|
+
β XMTP NetworkβββββΆβThin Server βββββΆβ QStash β
|
|
20
|
+
β β β(Enhanced) β β (Reliable) β
|
|
21
|
+
βββββββββββββββ βββββββββββββββ βββββββββββββββ
|
|
22
|
+
β
|
|
23
|
+
βΌ
|
|
24
|
+
βββββββββββββββ βββββββββββββββ
|
|
25
|
+
β Agent ββββββApp/Webhook β
|
|
26
|
+
β Processing β β (Scalable) β
|
|
27
|
+
βββββββββββββββ βββββββββββββββ
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### β
Built-in Reliability Features
|
|
31
|
+
|
|
32
|
+
1. **QStash Automatic Retries**: Built-in exponential backoff
|
|
33
|
+
2. **No Persistent Connections**: Eliminates connection drop issues
|
|
34
|
+
3. **Horizontal Scalability**: Multiple instances supported
|
|
35
|
+
4. **Dead Letter Queues**: Failed messages are preserved
|
|
36
|
+
5. **At-least-once Delivery**: Messages guaranteed to be processed
|
|
37
|
+
|
|
38
|
+
## π§ Enhanced Connection Management
|
|
39
|
+
|
|
40
|
+
### Basic Usage
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import {
|
|
44
|
+
createSigner,
|
|
45
|
+
createXMTPConnectionManager,
|
|
46
|
+
type XMTPConnectionConfig
|
|
47
|
+
} from "@hybrd/xmtp"
|
|
48
|
+
|
|
49
|
+
// Enhanced connection with reliability features
|
|
50
|
+
const signer = createSigner(process.env.XMTP_WALLET_KEY!)
|
|
51
|
+
|
|
52
|
+
const connectionConfig: XMTPConnectionConfig = {
|
|
53
|
+
maxRetries: 5, // Connection attempts
|
|
54
|
+
retryDelayMs: 1000, // Base retry delay
|
|
55
|
+
healthCheckIntervalMs: 30000, // Health check interval
|
|
56
|
+
connectionTimeoutMs: 15000, // Connection timeout
|
|
57
|
+
reconnectOnFailure: true // Auto-reconnect
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const connectionManager = await createXMTPConnectionManager(
|
|
61
|
+
signer,
|
|
62
|
+
connectionConfig
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// Get health metrics
|
|
66
|
+
const health = connectionManager.getHealth()
|
|
67
|
+
console.log('Connection Health:', health)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Advanced Production Usage
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { RobustXMTPService } from "@hybrd/xmtp/scripts/enhanced-connection-example"
|
|
74
|
+
|
|
75
|
+
const service = new RobustXMTPService()
|
|
76
|
+
await service.start()
|
|
77
|
+
|
|
78
|
+
// Process messages with automatic retry/reconnection
|
|
79
|
+
await service.processMessage(conversationId, "Hello!")
|
|
80
|
+
|
|
81
|
+
// Monitor health
|
|
82
|
+
const health = service.getConnectionHealth()
|
|
83
|
+
if (!health?.isConnected) {
|
|
84
|
+
console.warn("XMTP connection unhealthy")
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## π Connection Health Monitoring
|
|
89
|
+
|
|
90
|
+
The enhanced client provides real-time health metrics:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
interface XMTPConnectionHealth {
|
|
94
|
+
isConnected: boolean // Current connection status
|
|
95
|
+
lastHealthCheck: Date // Last health check timestamp
|
|
96
|
+
consecutiveFailures: number // Failed health checks in a row
|
|
97
|
+
totalReconnects: number // Total reconnection attempts
|
|
98
|
+
avgResponseTime: number // Average response time in ms
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## π Quick Start
|
|
103
|
+
|
|
104
|
+
### 1. Install Dependencies
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pnpm with-env pnpm --filter @hybrd/xmtp install
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 2. Run Enhanced Connection Demo
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pnpm with-env pnpm --filter @hybrd/xmtp enhanced:demo
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Integrate in Your Code
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Replace basic XMTP client creation
|
|
120
|
+
// OLD:
|
|
121
|
+
const client = await createXMTPClient(signer)
|
|
122
|
+
|
|
123
|
+
// NEW: With enhanced reliability
|
|
124
|
+
const connectionManager = await createXMTPConnectionManager(signer, {
|
|
125
|
+
maxRetries: 5,
|
|
126
|
+
healthCheckIntervalMs: 30000,
|
|
127
|
+
reconnectOnFailure: true
|
|
128
|
+
})
|
|
129
|
+
const client = connectionManager.getClient()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## π Why Your Current Architecture is Already Robust
|
|
133
|
+
|
|
134
|
+
Your **QStash-based webhook system** already provides superior reliability compared to traditional streaming:
|
|
135
|
+
|
|
136
|
+
### Traditional Streaming Issues β
|
|
137
|
+
- Connection drops require manual reconnection
|
|
138
|
+
- Memory leaks from long-running connections
|
|
139
|
+
- Difficult to scale horizontally
|
|
140
|
+
- Complex heartbeat/keepalive management
|
|
141
|
+
- Single point of failure
|
|
142
|
+
|
|
143
|
+
### Your QStash Architecture Benefits β
|
|
144
|
+
- **Automatic Retries**: QStash handles exponential backoff
|
|
145
|
+
- **No Connection Drops**: Stateless webhook calls
|
|
146
|
+
- **Horizontal Scaling**: Multiple app instances supported
|
|
147
|
+
- **Built-in Monitoring**: QStash provides delivery metrics
|
|
148
|
+
- **Dead Letter Queues**: Failed messages preserved
|
|
149
|
+
- **At-least-once Delivery**: Guaranteed message processing
|
|
150
|
+
|
|
151
|
+
## π§ Configuration Options
|
|
152
|
+
|
|
153
|
+
### Environment Variables
|
|
154
|
+
|
|
155
|
+
| Variable | Description | Default |
|
|
156
|
+
| --------------------- | ---------------------------------------- | --------------------------------------- |
|
|
157
|
+
| `XMTP_STORAGE_PATH` | Custom path for XMTP database storage | `.data/xmtp` (relative to project root) |
|
|
158
|
+
| `XMTP_WALLET_KEY` | Private key for XMTP wallet | Required |
|
|
159
|
+
| `XMTP_ENCRYPTION_KEY` | Encryption key for database | Required for persistent mode |
|
|
160
|
+
| `XMTP_ENV` | XMTP environment (`dev` or `production`) | `dev` |
|
|
161
|
+
| `PROJECT_ROOT` | Override project root path | Auto-detected |
|
|
162
|
+
|
|
163
|
+
### Connection Configuration
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
interface XMTPConnectionConfig {
|
|
167
|
+
maxRetries?: number // Default: 5
|
|
168
|
+
retryDelayMs?: number // Default: 1000ms
|
|
169
|
+
healthCheckIntervalMs?: number // Default: 30000ms
|
|
170
|
+
connectionTimeoutMs?: number // Default: 10000ms
|
|
171
|
+
reconnectOnFailure?: boolean // Default: true
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Custom Storage Location
|
|
176
|
+
|
|
177
|
+
You can specify a custom storage location for XMTP database files:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Absolute path
|
|
181
|
+
export XMTP_STORAGE_PATH=/custom/path/to/xmtp/storage
|
|
182
|
+
|
|
183
|
+
# Relative path (relative to current working directory)
|
|
184
|
+
export XMTP_STORAGE_PATH=./custom/xmtp/storage
|
|
185
|
+
|
|
186
|
+
# Use with pnpm with-env
|
|
187
|
+
pnpm with-env your-xmtp-command
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Testing Custom Storage
|
|
191
|
+
|
|
192
|
+
Run the custom storage example to test your configuration:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Test with default storage location
|
|
196
|
+
pnpm with-env pnpm --filter @hybrd/xmtp custom:storage
|
|
197
|
+
|
|
198
|
+
# Test with custom storage location
|
|
199
|
+
export XMTP_STORAGE_PATH=/tmp/my-custom-xmtp-storage
|
|
200
|
+
pnpm with-env pnpm --filter @hybrd/xmtp custom:storage
|
|
201
|
+
|
|
202
|
+
# Test with relative path
|
|
203
|
+
export XMTP_STORAGE_PATH=./my-xmtp-data
|
|
204
|
+
pnpm with-env pnpm --filter @hybrd/xmtp custom:storage
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## π οΈ Available Scripts
|
|
208
|
+
|
|
209
|
+
| Script | Command | Description |
|
|
210
|
+
| ------------------ | -------------------------------------------- | --------------------------------- |
|
|
211
|
+
| `gen:keys` | `pnpm --filter @hybrd/xmtp gen:keys` | Generate new XMTP wallet keys |
|
|
212
|
+
| `register` | `pnpm --filter @hybrd/xmtp register` | Register wallet on XMTP network |
|
|
213
|
+
| `revoke` | `pnpm --filter @hybrd/xmtp revoke` | Revoke old XMTP installations |
|
|
214
|
+
| `enhanced:demo` | `pnpm --filter @hybrd/xmtp enhanced:demo` | Demo enhanced connection features |
|
|
215
|
+
| `test:messages` | `pnpm --filter @hybrd/xmtp test:messages` | Test message reception |
|
|
216
|
+
| `refresh:identity` | `pnpm --filter @hybrd/xmtp refresh:identity` | Refresh XMTP identity |
|
|
217
|
+
| `custom:storage` | `pnpm --filter @hybrd/xmtp custom:storage` | Test custom storage configuration |
|
|
218
|
+
|
|
219
|
+
> **Note**: Always use `pnpm with-env` to ensure environment variables are loaded:
|
|
220
|
+
> ```bash
|
|
221
|
+
> pnpm with-env pnpm --filter @hybrd/xmtp <script-name>
|
|
222
|
+
> ```
|
|
223
|
+
|
|
224
|
+
## π― Best Practices
|
|
225
|
+
|
|
226
|
+
### 1. Use Connection Manager for Long-Running Services
|
|
227
|
+
```typescript
|
|
228
|
+
// For services that need persistent XMTP connections
|
|
229
|
+
const manager = await createXMTPConnectionManager(signer, config)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 2. Leverage Your Existing QStash Architecture
|
|
233
|
+
```typescript
|
|
234
|
+
// For message processing, your webhook system is ideal
|
|
235
|
+
// No changes needed - it's already robust!
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 3. Monitor Connection Health
|
|
239
|
+
```typescript
|
|
240
|
+
setInterval(() => {
|
|
241
|
+
const health = connectionManager.getHealth()
|
|
242
|
+
if (health.consecutiveFailures > 3) {
|
|
243
|
+
console.warn("XMTP connection degraded")
|
|
244
|
+
}
|
|
245
|
+
}, 60000)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 4. Use Environment Variables
|
|
249
|
+
```typescript
|
|
250
|
+
// Always use the project's environment wrapper
|
|
251
|
+
pnpm with-env [your-command]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## π§ͺ Testing
|
|
255
|
+
|
|
256
|
+
Run the enhanced connection demo to see health monitoring in action:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Start the demo (runs for 2 minutes showing health checks)
|
|
260
|
+
pnpm with-env pnpm --filter @hybrd/xmtp enhanced:demo
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## π Debugging
|
|
264
|
+
|
|
265
|
+
Enable debug logging:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
DEBUG=xmtp-sdk* pnpm with-env pnpm --filter @hybrd/xmtp enhanced:demo
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## π Metrics & Monitoring
|
|
272
|
+
|
|
273
|
+
The enhanced client provides detailed metrics:
|
|
274
|
+
|
|
275
|
+
- **Connection Status**: Real-time connection state
|
|
276
|
+
- **Response Times**: Average XMTP response latency
|
|
277
|
+
- **Failure Counts**: Track connection reliability
|
|
278
|
+
- **Reconnection Events**: Monitor stability over time
|
|
279
|
+
|
|
280
|
+
## π¨ Migration Guide
|
|
281
|
+
|
|
282
|
+
### From Basic XMTP Client
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// Before
|
|
286
|
+
const client = await createXMTPClient(signer)
|
|
287
|
+
|
|
288
|
+
// After
|
|
289
|
+
const manager = await createXMTPConnectionManager(signer)
|
|
290
|
+
const client = manager.getClient()
|
|
291
|
+
|
|
292
|
+
// Remember to cleanup
|
|
293
|
+
await manager.disconnect()
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Keep Your Webhook Architecture
|
|
297
|
+
|
|
298
|
+
**No changes needed!** Your QStash webhook system is already providing:
|
|
299
|
+
- β
Automatic retries with exponential backoff
|
|
300
|
+
- β
Reliable message delivery guarantees
|
|
301
|
+
- β
Horizontal scalability
|
|
302
|
+
- β
Built-in monitoring and alerting
|
|
303
|
+
- β
Dead letter queue handling
|
|
304
|
+
|
|
305
|
+
## π Additional Resources
|
|
306
|
+
|
|
307
|
+
- [XMTP Node SDK Documentation](https://xmtp.org/docs/build/get-started/overview)
|
|
308
|
+
- [QStash Documentation](https://upstash.com/docs/qstash)
|
|
309
|
+
- [Project Architecture](../../ARCHITECTURE.md)
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
Your system is already built with production-grade reliability. The enhanced XMTP client provides additional connection management features for edge cases, but your webhook-based architecture is the recommended approach for scalable XMTP integrations! π
|
|
314
|
+
|
|
315
|
+
# XMTP Package
|
|
316
|
+
|
|
317
|
+
This package provides XMTP client functionality and various resolvers for address, ENS, and basename resolution.
|
|
318
|
+
|
|
319
|
+
## Resolvers
|
|
320
|
+
|
|
321
|
+
### Master Resolver
|
|
322
|
+
|
|
323
|
+
The `Resolver` class provides a unified interface for all resolution types:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { Resolver } from '@hybrd/xmtp/resolver'
|
|
327
|
+
import { createPublicClient, http } from 'viem'
|
|
328
|
+
import { mainnet, base } from 'viem/chains'
|
|
329
|
+
|
|
330
|
+
// Create clients
|
|
331
|
+
const mainnetClient = createPublicClient({
|
|
332
|
+
chain: mainnet,
|
|
333
|
+
transport: http()
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
const baseClient = createPublicClient({
|
|
337
|
+
chain: base,
|
|
338
|
+
transport: http()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
// Initialize the master resolver
|
|
342
|
+
const resolver = new Resolver({
|
|
343
|
+
xmtpClient: yourXmtpClient,
|
|
344
|
+
mainnetClient,
|
|
345
|
+
baseClient,
|
|
346
|
+
maxCacheSize: 1000,
|
|
347
|
+
cacheTtl: 3600000 // 1 hour
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// Universal name resolution
|
|
351
|
+
const address = await resolver.resolveName('vitalik.eth')
|
|
352
|
+
const basenameAddress = await resolver.resolveName('myname.base.eth')
|
|
353
|
+
|
|
354
|
+
// Universal reverse resolution
|
|
355
|
+
const name = await resolver.resolveAddressToName('0x...')
|
|
356
|
+
|
|
357
|
+
// Get complete profile (ENS + basename data)
|
|
358
|
+
const profile = await resolver.getCompleteProfile('0x...')
|
|
359
|
+
|
|
360
|
+
// Individual resolver methods are also available
|
|
361
|
+
const ensName = await resolver.resolveENSName('vitalik.eth')
|
|
362
|
+
const basename = await resolver.getBasename('0x...')
|
|
363
|
+
const message = await resolver.findMessage('messageId')
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Individual Resolvers
|
|
367
|
+
|
|
368
|
+
You can also use individual resolvers directly:
|
|
369
|
+
|
|
370
|
+
- `AddressResolver` - XMTP address resolution
|
|
371
|
+
- `XmtpResolver` - XMTP message and address resolution with advanced features
|
|
372
|
+
- `ENSResolver` - ENS name resolution
|
|
373
|
+
- `BasenameResolver` - Basename resolution for Base network
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { ENSResolver, BasenameResolver } from '@hybrd/xmtp/resolver'
|
|
377
|
+
|
|
378
|
+
const ensResolver = new ENSResolver({ mainnetClient })
|
|
379
|
+
const basenameResolver = new BasenameResolver({ publicClient: baseClient })
|
|
380
|
+
```
|
package/biome.jsonc
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hybrd/xmtp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./src/index.ts",
|
|
8
|
+
"types": "./src/index.ts"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@coinbase/coinbase-sdk": "^0.22.0",
|
|
13
|
+
"@xmtp/content-type-group-updated": "2.0.2",
|
|
14
|
+
"@xmtp/content-type-reaction": "2.0.2",
|
|
15
|
+
"@xmtp/content-type-reply": "2.0.2",
|
|
16
|
+
"@xmtp/content-type-text": "2.0.2",
|
|
17
|
+
"@xmtp/content-type-transaction-reference": "2.0.2",
|
|
18
|
+
"@xmtp/content-type-wallet-send-calls": "2.0.0",
|
|
19
|
+
"@xmtp/node-sdk": "^4.0.1",
|
|
20
|
+
"jsonwebtoken": "^9.0.2",
|
|
21
|
+
"uint8arrays": "^5.1.0",
|
|
22
|
+
"viem": "^2.22.17"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/jsonwebtoken": "^9.0.7",
|
|
26
|
+
"@types/node": "22.8.6",
|
|
27
|
+
"vitest": "^3.2.4",
|
|
28
|
+
"@hybrd/biome": "0.0.0",
|
|
29
|
+
"@hybrd/tsconfig": "0.0.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"clean": "rm -rf .turbo",
|
|
33
|
+
"gen:keys": "tsx scripts/generate-keys.ts",
|
|
34
|
+
"register": "tsx scripts/register-wallet.ts",
|
|
35
|
+
"revoke": "tsx scripts/revoke-installations.ts",
|
|
36
|
+
"revoke:all": "tsx scripts/revoke-all-installations.ts",
|
|
37
|
+
"enhanced:demo": "tsx scripts/enhanced-connection-example.ts",
|
|
38
|
+
"test:messages": "tsx scripts/test-message-reception.ts",
|
|
39
|
+
"refresh:identity": "tsx scripts/refresh-identity.ts",
|
|
40
|
+
"custom:storage": "tsx scripts/custom-storage-example.ts",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"lint": "biome lint --unsafe",
|
|
43
|
+
"lint:fix": "biome lint --write --unsafe",
|
|
44
|
+
"format": "biome format --write"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { generateEncryptionKeyHex } from "@hybrd/xmtp"
|
|
2
|
+
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
|
|
3
|
+
|
|
4
|
+
// Check Node.js version
|
|
5
|
+
const nodeVersion = process.versions.node
|
|
6
|
+
const [major] = nodeVersion?.split(".").map(Number) || [0]
|
|
7
|
+
|
|
8
|
+
if (major && major < 20) {
|
|
9
|
+
console.error("Error: Node.js version 20 or higher is required")
|
|
10
|
+
process.exit(1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.log("Generating XMTP keys...")
|
|
14
|
+
|
|
15
|
+
const walletKey = generatePrivateKey()
|
|
16
|
+
const account = privateKeyToAccount(walletKey)
|
|
17
|
+
const encryptionKeyHex = generateEncryptionKeyHex()
|
|
18
|
+
const publicKey = account.address
|
|
19
|
+
|
|
20
|
+
console.log("\n# === Generated Keys ===")
|
|
21
|
+
console.log(`# Public Address: ${publicKey}`)
|
|
22
|
+
console.log(`XMTP_WALLET_KEY=${walletKey}`)
|
|
23
|
+
console.log(`XMTP_ENCRYPTION_KEY=${encryptionKeyHex}`)
|
|
24
|
+
|
|
25
|
+
console.log("\nCopy the above environment variables to your .env file")
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createXMTPClient,
|
|
3
|
+
logAgentDetails,
|
|
4
|
+
validateEnvironment
|
|
5
|
+
} from "../src/client"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Refresh XMTP Identity Script
|
|
9
|
+
*
|
|
10
|
+
* This script refreshes the bot's XMTP identity on the production network
|
|
11
|
+
* to resolve association errors and missing identity updates
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
async function refreshXMTPIdentity() {
|
|
15
|
+
console.log("π XMTP Identity Refresh")
|
|
16
|
+
console.log("========================")
|
|
17
|
+
|
|
18
|
+
// Validate environment
|
|
19
|
+
const { XMTP_WALLET_KEY } = validateEnvironment(["XMTP_WALLET_KEY"])
|
|
20
|
+
|
|
21
|
+
if (!XMTP_WALLET_KEY) {
|
|
22
|
+
console.error("β XMTP_WALLET_KEY is required")
|
|
23
|
+
process.exit(1)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
console.log(`π Environment: ${process.env.XMTP_ENV || "dev"}`)
|
|
28
|
+
|
|
29
|
+
// Step 1: Create client with persistence to force identity refresh
|
|
30
|
+
console.log("\nπ Step 1: Creating client with persistence...")
|
|
31
|
+
const persistentClient = await createXMTPClient(XMTP_WALLET_KEY, {
|
|
32
|
+
persist: true
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
console.log(
|
|
36
|
+
`π Wallet Address: ${persistentClient.accountIdentifier?.identifier}`
|
|
37
|
+
)
|
|
38
|
+
console.log(`π XMTP Environment: ${process.env.XMTP_ENV || "dev"}`)
|
|
39
|
+
|
|
40
|
+
// Step 2: Force full sync
|
|
41
|
+
console.log("\nπ Step 2: Forcing full conversation sync...")
|
|
42
|
+
await persistentClient.conversations.sync()
|
|
43
|
+
|
|
44
|
+
// Step 3: List conversations
|
|
45
|
+
const conversations = await persistentClient.conversations.list()
|
|
46
|
+
console.log(`π¬ Found ${conversations.length} conversations`)
|
|
47
|
+
|
|
48
|
+
// Step 4: Display agent details
|
|
49
|
+
console.log("\nπ Step 3: Displaying refreshed identity details...")
|
|
50
|
+
await logAgentDetails(persistentClient)
|
|
51
|
+
|
|
52
|
+
// Step 5: Test identity resolution
|
|
53
|
+
console.log("\nπ Step 4: Testing identity resolution...")
|
|
54
|
+
|
|
55
|
+
if (conversations.length > 0) {
|
|
56
|
+
const testConv = conversations[0]
|
|
57
|
+
if (!testConv) {
|
|
58
|
+
console.log("β No valid conversation found")
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Get all participants
|
|
64
|
+
const members = await testConv.members()
|
|
65
|
+
console.log(`π₯ Conversation members: ${members.length}`)
|
|
66
|
+
|
|
67
|
+
for (const member of members) {
|
|
68
|
+
console.log(` - Inbox ID: ${member.inboxId}`)
|
|
69
|
+
console.log(` - Installation IDs: ${member.installationIds.length}`)
|
|
70
|
+
|
|
71
|
+
// Try to resolve addresses
|
|
72
|
+
try {
|
|
73
|
+
const inboxState =
|
|
74
|
+
await persistentClient.preferences.inboxStateFromInboxIds([
|
|
75
|
+
member.inboxId
|
|
76
|
+
])
|
|
77
|
+
if (
|
|
78
|
+
inboxState.length > 0 &&
|
|
79
|
+
inboxState[0] &&
|
|
80
|
+
inboxState[0].identifiers.length > 0
|
|
81
|
+
) {
|
|
82
|
+
const identifier = inboxState[0]?.identifiers[0]?.identifier
|
|
83
|
+
console.log(` - Address: ${identifier || "Unable to resolve"}`)
|
|
84
|
+
} else {
|
|
85
|
+
console.log(` - Address: Unable to resolve`)
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const err = error as Error
|
|
89
|
+
console.log(` - Address: Error resolving - ${err.message}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
const err = error as Error
|
|
94
|
+
console.log(`β Error testing conversation: ${err.message}`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log("\nβ
Identity refresh completed successfully!")
|
|
99
|
+
console.log("π Try processing messages again")
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const err = error as Error
|
|
102
|
+
console.error("β Identity refresh failed:", err)
|
|
103
|
+
|
|
104
|
+
if (err.message.includes("XMTP_ENCRYPTION_KEY")) {
|
|
105
|
+
console.log("\nπ‘ Add XMTP_ENCRYPTION_KEY to your environment:")
|
|
106
|
+
console.log(" export XMTP_ENCRYPTION_KEY=your_key_here")
|
|
107
|
+
console.log(
|
|
108
|
+
" Or run: pnpm with-env pnpm --filter @hybrd/xmtp refresh:identity"
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
process.exit(1)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Run the refresh
|
|
117
|
+
if (require.main === module) {
|
|
118
|
+
refreshXMTPIdentity().catch(console.error)
|
|
119
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createSigner, createXMTPClient, getDbPath, logAgentDetails, validateEnvironment } from "../src/client"
|
|
2
|
+
|
|
3
|
+
async function registerOnProduction() {
|
|
4
|
+
console.log("π Starting XMTP Production Network Registration...")
|
|
5
|
+
|
|
6
|
+
// Validate required environment variables
|
|
7
|
+
const { XMTP_WALLET_KEY } = validateEnvironment([
|
|
8
|
+
"XMTP_WALLET_KEY",
|
|
9
|
+
"XMTP_ENCRYPTION_KEY"
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
if (!XMTP_WALLET_KEY) {
|
|
13
|
+
console.error("β XMTP_WALLET_KEY is required for registration")
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
console.log("π Creating signer...")
|
|
19
|
+
const signer = createSigner(XMTP_WALLET_KEY)
|
|
20
|
+
|
|
21
|
+
// Get wallet address for logging
|
|
22
|
+
const identifier = await signer.getIdentifier()
|
|
23
|
+
const address = identifier.identifier
|
|
24
|
+
console.log(`π Wallet Address: ${address}`)
|
|
25
|
+
|
|
26
|
+
console.log("π Connecting to XMTP Production Network...")
|
|
27
|
+
console.log("β οΈ This will prompt you to sign messages in your wallet")
|
|
28
|
+
console.log(" - 'XMTP : Authenticate to inbox' message")
|
|
29
|
+
console.log(" - 'Grant messaging access to app' message")
|
|
30
|
+
console.log(" - 'Create inbox' message (if first time)")
|
|
31
|
+
|
|
32
|
+
// Use getDbPath to ensure directory creation and proper path handling
|
|
33
|
+
const dbPath = getDbPath(`production-${address}`)
|
|
34
|
+
console.log(`π Database path: ${dbPath}`)
|
|
35
|
+
|
|
36
|
+
// Connect to production network
|
|
37
|
+
const client = await createXMTPClient(XMTP_WALLET_KEY)
|
|
38
|
+
|
|
39
|
+
console.log("β
Successfully connected to XMTP Production Network!")
|
|
40
|
+
|
|
41
|
+
// Log client details
|
|
42
|
+
await logAgentDetails(client)
|
|
43
|
+
|
|
44
|
+
console.log("π‘ Syncing conversations...")
|
|
45
|
+
await client.conversations.sync()
|
|
46
|
+
|
|
47
|
+
const conversations = await client.conversations.list()
|
|
48
|
+
console.log(`π¬ Found ${conversations.length} existing conversations`)
|
|
49
|
+
|
|
50
|
+
console.log("π Registration Complete!")
|
|
51
|
+
console.log(`
|
|
52
|
+
β Wallet ${address} is now registered on XMTP Production Network
|
|
53
|
+
β Inbox ID: ${client.inboxId}
|
|
54
|
+
β Database: production-${address}.db3
|
|
55
|
+
β Ready to receive messages on production network
|
|
56
|
+
|
|
57
|
+
Next steps:
|
|
58
|
+
1. Update your environment: XMTP_ENV=production
|
|
59
|
+
2. Start your listener service
|
|
60
|
+
3. Share your address for others to message: ${address}
|
|
61
|
+
4. Test messaging at: https://xmtp.chat/dm/${address}
|
|
62
|
+
`)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("β Registration failed:", error)
|
|
65
|
+
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
if (error.message.includes("User rejected")) {
|
|
68
|
+
console.log(
|
|
69
|
+
"π Registration was cancelled. You need to approve the wallet signatures to complete registration."
|
|
70
|
+
)
|
|
71
|
+
} else if (error.message.includes("network")) {
|
|
72
|
+
console.log(
|
|
73
|
+
"π Network connection issue. Please check your internet connection and try again."
|
|
74
|
+
)
|
|
75
|
+
} else if (
|
|
76
|
+
error.message.includes("database") ||
|
|
77
|
+
error.message.includes("Unable to open")
|
|
78
|
+
) {
|
|
79
|
+
console.log(
|
|
80
|
+
"πΎ Database access issue. Please check file permissions and ensure the directory exists."
|
|
81
|
+
)
|
|
82
|
+
} else {
|
|
83
|
+
console.log("π‘ Make sure your wallet is connected and try again.")
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
process.exit(1)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Run registration
|
|
92
|
+
registerOnProduction().catch((error) => {
|
|
93
|
+
console.error("π₯ Fatal error during registration:", error)
|
|
94
|
+
process.exit(1)
|
|
95
|
+
})
|