@toon-protocol/client 0.4.2

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.
@@ -0,0 +1,966 @@
1
+ import * as _toon_protocol_core from '@toon-protocol/core';
2
+ import { IlpPeerInfo, IlpSendResult, IlpClient, ConnectorAdminClient, ConnectorChannelClient, OpenChannelParams, OpenChannelResult, ChannelState } from '@toon-protocol/core';
3
+ import { NostrEvent } from 'nostr-tools/pure';
4
+ import { PrivateKeyAccount } from 'viem/accounts';
5
+
6
+ /**
7
+ * Configuration for ToonClient.
8
+ *
9
+ * This story implements HTTP mode only. Embedded mode will be added in a future epic.
10
+ *
11
+ * @example HTTP Mode (implemented)
12
+ * ```typescript
13
+ * const client = new ToonClient({
14
+ * connectorUrl: 'http://localhost:8080',
15
+ * secretKey,
16
+ * ilpInfo: { ilpAddress, btpEndpoint, pubkey },
17
+ * toonEncoder: encodeEvent,
18
+ * toonDecoder: decodeEvent,
19
+ * });
20
+ * ```
21
+ *
22
+ * @example Embedded Mode (not yet implemented)
23
+ * ```typescript
24
+ * const client = new ToonClient({
25
+ * connector: embeddedConnectorInstance, // Will throw error: "Embedded mode not yet implemented"
26
+ * secretKey,
27
+ * ilpInfo,
28
+ * toonEncoder,
29
+ * toonDecoder,
30
+ * });
31
+ * ```
32
+ */
33
+ interface ToonClientConfig {
34
+ /**
35
+ * HTTP URL of external connector service.
36
+ * Required for HTTP mode.
37
+ * Example: 'http://localhost:8080'
38
+ */
39
+ connectorUrl?: string;
40
+ /**
41
+ * Embedded connector instance - NOT IMPLEMENTED in this story.
42
+ * Will throw error: "Embedded mode not yet implemented in ToonClient."
43
+ * Reserved for future implementation.
44
+ */
45
+ connector?: unknown;
46
+ /**
47
+ * 32-byte Nostr private key (hex or Uint8Array).
48
+ * Optional — if omitted, a keypair is auto-generated in applyDefaults().
49
+ */
50
+ secretKey?: Uint8Array;
51
+ /** ILP peer information for this client */
52
+ ilpInfo: IlpPeerInfo;
53
+ /** Function to encode Nostr events to TOON binary format */
54
+ toonEncoder: (event: NostrEvent) => Uint8Array;
55
+ /** Function to decode TOON binary format to Nostr events */
56
+ toonDecoder: (bytes: Uint8Array) => NostrEvent;
57
+ /**
58
+ * EVM private key for signing balance proofs and on-chain transactions.
59
+ *
60
+ * By default, this is derived from `secretKey` — both Nostr and EVM use
61
+ * secp256k1, so a single key provides both identities (matching the SDK's
62
+ * `fromMnemonic()`/`fromSecretKey()` behavior).
63
+ *
64
+ * Only set this if you need a *different* EVM key than your Nostr key
65
+ * (e.g., hardware wallet, custodial key, or legacy key separation).
66
+ */
67
+ evmPrivateKey?: string | Uint8Array;
68
+ /** Supported settlement chain identifiers (e.g., ["evm:anvil:31337"]) */
69
+ supportedChains?: string[];
70
+ /** Maps chain identifier to EVM settlement address */
71
+ settlementAddresses?: Record<string, string>;
72
+ /** Maps chain identifier to preferred token contract address */
73
+ preferredTokens?: Record<string, string>;
74
+ /** Maps chain identifier to TokenNetwork contract address (EVM only) */
75
+ tokenNetworks?: Record<string, string>;
76
+ /** BTP WebSocket URL (e.g., "ws://localhost:3000") */
77
+ btpUrl?: string;
78
+ /** Auth token for BTP handshake */
79
+ btpAuthToken?: string;
80
+ /** Peer ID for BTP connection (used in connector env var BTP_PEER_{ID}_SECRET) */
81
+ btpPeerId?: string;
82
+ /**
83
+ * ILP destination address for event publishing.
84
+ * Defaults to the connector's local address (derived from connectorUrl host).
85
+ * For multi-hop routing, set this to the target node's ILP address.
86
+ * Examples:
87
+ * - 'g.toon.genesis' - Publish to genesis node
88
+ * - 'g.toon.peer1' - Publish to peer1 node
89
+ */
90
+ destinationAddress?: string;
91
+ /** Maps chain identifier to RPC URL (e.g., {"evm:anvil:31337": "http://localhost:8545"}) */
92
+ chainRpcUrls?: Record<string, string>;
93
+ /** Amount to deposit when opening channel (default: "0") */
94
+ initialDeposit?: string;
95
+ /** Challenge period in seconds (default: 86400) */
96
+ settlementTimeout?: number;
97
+ /** File path for persisting payment channel nonce/amount state across restarts */
98
+ channelStorePath?: string;
99
+ /** Nostr relay URL for peer discovery. Default: 'ws://localhost:7100' */
100
+ relayUrl?: string;
101
+ /**
102
+ * Known peers to bootstrap with.
103
+ * If provided, these peers will be used for initial bootstrap.
104
+ * DiscoveryTracker will discover additional peers from kind:10032 events after bootstrap.
105
+ */
106
+ knownPeers?: {
107
+ pubkey: string;
108
+ relayUrl: string;
109
+ btpEndpoint?: string;
110
+ }[];
111
+ /** Query timeout in milliseconds. Default: 30000 */
112
+ queryTimeout?: number;
113
+ /** Maximum number of retries for failed operations. Default: 3 */
114
+ maxRetries?: number;
115
+ /** Delay between retries in milliseconds. Default: 1000 */
116
+ retryDelay?: number;
117
+ }
118
+ /**
119
+ * Result returned by ToonClient.start()
120
+ */
121
+ interface ToonStartResult {
122
+ /** Number of peers discovered during bootstrap */
123
+ peersDiscovered: number;
124
+ /** Mode the client is running in */
125
+ mode: 'http' | 'embedded';
126
+ }
127
+ /**
128
+ * Result returned by ToonClient.publishEvent()
129
+ */
130
+ interface PublishEventResult {
131
+ /** Whether the event was successfully published */
132
+ success: boolean;
133
+ /** ID of the published event */
134
+ eventId?: string;
135
+ /** ILP fulfillment from the relay (proof of payment) */
136
+ fulfillment?: string;
137
+ /** Error message if success is false */
138
+ error?: string;
139
+ }
140
+ /**
141
+ * Parameters for signing a balance proof.
142
+ */
143
+ interface BalanceProofParams {
144
+ /** Payment channel identifier */
145
+ channelId: string;
146
+ /** Monotonically increasing nonce */
147
+ nonce: number;
148
+ /** Cumulative amount transferred */
149
+ transferredAmount: bigint;
150
+ /** Amount locked in pending transfers */
151
+ lockedAmount: bigint;
152
+ /** Merkle root of pending lock hashes */
153
+ locksRoot: string;
154
+ }
155
+ /**
156
+ * A signed balance proof with EIP-712 signature.
157
+ */
158
+ interface SignedBalanceProof extends BalanceProofParams {
159
+ /** EIP-712 signature */
160
+ signature: string;
161
+ /** Address of the signer */
162
+ signerAddress: string;
163
+ }
164
+
165
+ /**
166
+ * ToonClient - High-level client for interacting with TOON network.
167
+ *
168
+ * This story implements HTTP mode only. Embedded mode will be added in a future epic.
169
+ *
170
+ * @example HTTP Mode
171
+ * ```typescript
172
+ * import { ToonClient } from '@toon-protocol/client';
173
+ * import { generateSecretKey, getPublicKey } from 'nostr-tools/pure';
174
+ * import { encodeEvent, decodeEvent } from '@toon-protocol/relay';
175
+ *
176
+ * const secretKey = generateSecretKey();
177
+ * const pubkey = getPublicKey(secretKey);
178
+ *
179
+ * const client = new ToonClient({
180
+ * connectorUrl: 'http://localhost:8080',
181
+ * secretKey,
182
+ * ilpInfo: {
183
+ * pubkey,
184
+ * ilpAddress: `g.toon.${pubkey.slice(0, 8)}`,
185
+ * btpEndpoint: 'ws://localhost:3000',
186
+ * },
187
+ * toonEncoder: encodeEvent,
188
+ * toonDecoder: decodeEvent,
189
+ * });
190
+ *
191
+ * await client.start(); // Bootstrap peers, start monitoring
192
+ *
193
+ * // Publish to default destination (from config)
194
+ * await client.publishEvent(signedEvent);
195
+ *
196
+ * // Publish to specific destination (multi-hop routing)
197
+ * await client.publishEvent(signedEvent, { destination: 'g.toon.peer1' });
198
+ *
199
+ * await client.stop(); // Cleanup
200
+ * ```
201
+ */
202
+ declare class ToonClient {
203
+ private readonly config;
204
+ private state;
205
+ private readonly evmSigner?;
206
+ private channelManager?;
207
+ /**
208
+ * Creates a new ToonClient instance.
209
+ *
210
+ * @param config - Client configuration
211
+ * @throws {ValidationError} If configuration is invalid
212
+ */
213
+ constructor(config: ToonClientConfig);
214
+ /**
215
+ * Generates a new Nostr keypair.
216
+ *
217
+ * @returns Object with secretKey (Uint8Array) and pubkey (hex string)
218
+ */
219
+ static generateKeypair(): {
220
+ secretKey: Uint8Array;
221
+ pubkey: string;
222
+ };
223
+ /**
224
+ * Gets the Nostr public key derived from the secret key.
225
+ * Works before start() is called.
226
+ */
227
+ getPublicKey(): string;
228
+ /**
229
+ * Gets the EVM address derived from the Nostr secret key (or explicit evmPrivateKey override).
230
+ */
231
+ getEvmAddress(): string | undefined;
232
+ /**
233
+ * Starts the ToonClient.
234
+ *
235
+ * This will:
236
+ * 1. Initialize HTTP mode components (runtime client, admin client, bootstrap, monitor)
237
+ * 2. Bootstrap the network (discover peers, register, and open channels)
238
+ * 3. Start monitoring relay for new peers (kind:10032 events)
239
+ *
240
+ * @returns Result with number of peers discovered and mode
241
+ * @throws {ToonClientError} If client is already started
242
+ * @throws {ToonClientError} If initialization fails
243
+ */
244
+ start(): Promise<ToonStartResult>;
245
+ /**
246
+ * Publishes a Nostr event to the relay via ILP payment.
247
+ *
248
+ * The event must already be finalized (signed with id, pubkey, sig).
249
+ *
250
+ * @param event - Signed Nostr event to publish
251
+ * @param options - Optional options including destination and signed balance proof claim
252
+ * @returns Result with success status, event ID, and fulfillment
253
+ * @throws {ToonClientError} If client is not started
254
+ * @throws {ToonClientError} If event publishing fails
255
+ */
256
+ publishEvent(event: NostrEvent, options?: {
257
+ destination?: string;
258
+ claim?: SignedBalanceProof;
259
+ }): Promise<PublishEventResult>;
260
+ /**
261
+ * Signs a balance proof for the given channel with the specified amount.
262
+ * Delegates to ChannelManager which auto-increments nonce and tracks cumulative amount.
263
+ *
264
+ * @param channelId - Payment channel identifier
265
+ * @param amount - Additional amount to add to cumulative transferred amount
266
+ * @returns Signed balance proof
267
+ * @throws {ToonClientError} If no EVM signer configured or channel not tracked
268
+ */
269
+ signBalanceProof(channelId: string, amount: bigint): Promise<SignedBalanceProof>;
270
+ /**
271
+ * Gets list of tracked payment channel IDs.
272
+ */
273
+ getTrackedChannels(): string[];
274
+ /**
275
+ * Sends an ILP payment, optionally with a balance proof claim via BTP.
276
+ *
277
+ * @param params - Payment parameters
278
+ * @returns ILP send result
279
+ * @throws {ToonClientError} If client is not started
280
+ */
281
+ sendPayment(params: {
282
+ destination: string;
283
+ amount: string;
284
+ data?: string;
285
+ claim?: SignedBalanceProof;
286
+ }): Promise<IlpSendResult>;
287
+ /**
288
+ * Stops the ToonClient and cleans up resources.
289
+ *
290
+ * This will:
291
+ * 1. Disconnect BTP client if connected
292
+ * 2. Clear internal state
293
+ *
294
+ * @throws {ToonClientError} If client is not started
295
+ */
296
+ stop(): Promise<void>;
297
+ /**
298
+ * Returns true if the client is currently started.
299
+ */
300
+ isStarted(): boolean;
301
+ /**
302
+ * Gets the number of peers discovered during bootstrap.
303
+ *
304
+ * @returns Number of peers discovered
305
+ * @throws {ToonClientError} If client is not started
306
+ */
307
+ getPeersCount(): number;
308
+ /**
309
+ * Gets the list of peers discovered by the relay monitor.
310
+ *
311
+ * @returns Array of discovered peer objects
312
+ * @throws {ToonClientError} If client is not started
313
+ */
314
+ getDiscoveredPeers(): _toon_protocol_core.DiscoveredPeer[];
315
+ }
316
+
317
+ /**
318
+ * Base error class for all TOON client errors.
319
+ */
320
+ declare class ToonClientError extends Error {
321
+ readonly code: string;
322
+ constructor(message: string, code: string, cause?: Error);
323
+ }
324
+ /**
325
+ * Network error for connection failures (ECONNREFUSED, ETIMEDOUT).
326
+ * These errors trigger retry logic with exponential backoff.
327
+ */
328
+ declare class NetworkError extends ToonClientError {
329
+ constructor(message: string, cause?: Error);
330
+ }
331
+ /**
332
+ * Connector error for 5xx server errors.
333
+ * These errors indicate the connector is unavailable or malfunctioning.
334
+ */
335
+ declare class ConnectorError extends ToonClientError {
336
+ constructor(message: string, cause?: Error);
337
+ }
338
+ /**
339
+ * Validation error for invalid input parameters.
340
+ * These errors are thrown before making any HTTP requests.
341
+ */
342
+ declare class ValidationError extends ToonClientError {
343
+ constructor(message: string, cause?: Error);
344
+ }
345
+
346
+ /**
347
+ * Configuration options for HttpRuntimeClient.
348
+ */
349
+ interface HttpRuntimeClientConfig {
350
+ /** Connector runtime API base URL (e.g., 'http://localhost:8080') */
351
+ connectorUrl: string;
352
+ /** Request timeout in milliseconds (default: 30000) */
353
+ timeout?: number;
354
+ /** Maximum retry attempts for network failures (default: 3) */
355
+ maxRetries?: number;
356
+ /** Initial retry delay in milliseconds (default: 1000) */
357
+ retryDelay?: number;
358
+ /** HTTP client implementation (for testing) */
359
+ httpClient?: typeof fetch;
360
+ }
361
+ /**
362
+ * HTTP client for sending ILP packets to an external connector runtime API.
363
+ *
364
+ * Implements the IlpClient interface for use with TOON agents
365
+ * that need to send ILP packets without embedding a full connector.
366
+ *
367
+ * Features:
368
+ * - Request validation (destination, amount, data)
369
+ * - Retry logic with exponential backoff for transient network failures
370
+ * - Typed error handling (NetworkError, ConnectorError, ValidationError)
371
+ * - Connection pooling and keep-alive (via Node.js fetch)
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * const client = new HttpRuntimeClient({
376
+ * connectorUrl: 'http://localhost:8080'
377
+ * });
378
+ *
379
+ * const result = await client.sendIlpPacket({
380
+ * destination: 'g.toon.alice',
381
+ * amount: '1000',
382
+ * data: 'base64EncodedToonData==',
383
+ * });
384
+ *
385
+ * if (result.accepted) {
386
+ * console.log('Payment accepted:', result.fulfillment);
387
+ * } else {
388
+ * console.error('Payment rejected:', result.code, result.message);
389
+ * }
390
+ * ```
391
+ */
392
+ declare class HttpRuntimeClient implements IlpClient {
393
+ private readonly connectorUrl;
394
+ private readonly timeout;
395
+ private readonly retryConfig;
396
+ private readonly httpClient;
397
+ constructor(config: HttpRuntimeClientConfig);
398
+ /**
399
+ * Send an ILP packet to the connector runtime API.
400
+ *
401
+ * @param params - ILP packet parameters
402
+ * @returns ILP packet response with acceptance status
403
+ * @throws {ValidationError} If request parameters are invalid
404
+ * @throws {NetworkError} If network connection fails after retries
405
+ * @throws {ConnectorError} If connector returns 5xx server error
406
+ */
407
+ sendIlpPacket(params: {
408
+ destination: string;
409
+ amount: string;
410
+ data: string;
411
+ timeout?: number;
412
+ }): Promise<IlpSendResult>;
413
+ /**
414
+ * Validate ILP packet request parameters.
415
+ *
416
+ * @throws {ValidationError} If any parameter is invalid
417
+ */
418
+ private validateRequest;
419
+ /**
420
+ * Send HTTP POST request to connector runtime API.
421
+ *
422
+ * @throws {NetworkError} On connection failures (ECONNREFUSED, ETIMEDOUT)
423
+ * @throws {ConnectorError} On 5xx server errors
424
+ * @returns IlpSendResult with acceptance status
425
+ */
426
+ private sendHttpRequest;
427
+ }
428
+
429
+ /**
430
+ * Configuration for HttpConnectorAdmin.
431
+ */
432
+ interface HttpConnectorAdminConfig {
433
+ /** Admin API base URL (e.g., 'http://localhost:8081') */
434
+ adminUrl: string;
435
+ /** Request timeout in milliseconds (default: 30000) */
436
+ timeout?: number;
437
+ /** Maximum retry attempts for network failures (default: 3) */
438
+ maxRetries?: number;
439
+ /** Initial retry delay in milliseconds (default: 1000) */
440
+ retryDelay?: number;
441
+ /** HTTP client for testing (default: global fetch) */
442
+ httpClient?: typeof fetch;
443
+ }
444
+ /**
445
+ * Result of a bulk peer operation.
446
+ */
447
+ interface PeerOperationResult {
448
+ /** Peer ID that was operated on */
449
+ peerId: string;
450
+ /** Whether the operation succeeded */
451
+ success: boolean;
452
+ /** Error that occurred (if failed) */
453
+ error?: Error;
454
+ }
455
+ /**
456
+ * HTTP-based connector admin client for managing ILP peers via REST API.
457
+ *
458
+ * Implements the ConnectorAdminClient interface using HTTP requests to the
459
+ * connector's admin API (typically port 8081).
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * // Embedded mode (DirectConnectorAdmin)
464
+ * const adminClient = new DirectConnectorAdmin(connectorNode);
465
+ *
466
+ * // HTTP mode (HttpConnectorAdmin)
467
+ * const adminClient = new HttpConnectorAdmin({
468
+ * adminUrl: 'http://localhost:8081'
469
+ * });
470
+ *
471
+ * // Add peer
472
+ * await adminClient.addPeer({
473
+ * id: 'nostr-abc123',
474
+ * url: 'btp+ws://alice.example.com:3000',
475
+ * authToken: 'secret-token',
476
+ * routes: [{ prefix: 'g.toon.alice' }]
477
+ * });
478
+ *
479
+ * // Remove peer
480
+ * await adminClient.removePeer('nostr-abc123');
481
+ * ```
482
+ *
483
+ * @throws {ValidationError} Input validation failed (before HTTP request)
484
+ * @throws {NetworkError} Connection failed (ECONNREFUSED, ETIMEDOUT)
485
+ * @throws {UnauthorizedError} Admin API returned 401 (missing/invalid auth)
486
+ * @throws {PeerAlreadyExistsError} Admin API returned 409 (duplicate peer)
487
+ * @throws {PeerNotFoundError} Admin API returned 404 (peer not found)
488
+ * @throws {ConnectorError} Admin API returned 5xx (server error)
489
+ */
490
+ declare class HttpConnectorAdmin implements ConnectorAdminClient {
491
+ private readonly adminUrl;
492
+ private readonly timeout;
493
+ private readonly retryConfig;
494
+ private readonly httpClient;
495
+ constructor(config: HttpConnectorAdminConfig);
496
+ /**
497
+ * Add a peer to the connector via the admin API.
498
+ *
499
+ * Validates peer config parameters and sends HTTP POST to /admin/peers.
500
+ *
501
+ * @param config - Peer configuration
502
+ * @param config.id - Unique peer identifier (non-empty string)
503
+ * @param config.url - BTP WebSocket URL (must start with 'btp+ws://' or 'btp+wss://')
504
+ * @param config.authToken - Authentication token (non-empty string)
505
+ * @param config.routes - Optional routing table entries
506
+ * @param config.settlement - Optional settlement configuration
507
+ *
508
+ * @throws {ValidationError} Invalid peer config (missing id, invalid url, etc.)
509
+ * @throws {PeerAlreadyExistsError} Peer with same ID already exists (409 Conflict)
510
+ * @throws {UnauthorizedError} Admin API authentication failed (401)
511
+ * @throws {NetworkError} Connection to admin API failed
512
+ * @throws {ConnectorError} Admin API server error (5xx)
513
+ */
514
+ addPeer(config: {
515
+ id: string;
516
+ url: string;
517
+ authToken: string;
518
+ routes?: {
519
+ prefix: string;
520
+ priority?: number;
521
+ }[];
522
+ settlement?: {
523
+ preference: string;
524
+ evmAddress?: string;
525
+ tokenAddress?: string;
526
+ tokenNetworkAddress?: string;
527
+ chainId?: number;
528
+ channelId?: string;
529
+ initialDeposit?: string;
530
+ };
531
+ }): Promise<void>;
532
+ /**
533
+ * Remove a peer from the connector via the admin API.
534
+ *
535
+ * Sends HTTP DELETE to /admin/peers/:id.
536
+ *
537
+ * @param peerId - Unique peer identifier to remove (non-empty string)
538
+ *
539
+ * @throws {ValidationError} Invalid peerId (empty string)
540
+ * @throws {PeerNotFoundError} Peer does not exist (404 Not Found)
541
+ * @throws {UnauthorizedError} Admin API authentication failed (401)
542
+ * @throws {NetworkError} Connection to admin API failed
543
+ * @throws {ConnectorError} Admin API server error (5xx)
544
+ */
545
+ removePeer(peerId: string): Promise<void>;
546
+ /**
547
+ * Add multiple peers in parallel for efficient bootstrapping.
548
+ *
549
+ * Uses Promise.allSettled() to execute peer additions concurrently,
550
+ * returning results for each operation regardless of individual failures.
551
+ *
552
+ * @param configs - Array of peer configurations to add
553
+ * @returns Array of results indicating success/failure for each peer
554
+ *
555
+ * @example
556
+ * ```typescript
557
+ * const results = await admin.addPeers([
558
+ * { id: 'peer1', url: 'btp+ws://...', authToken: 'token1' },
559
+ * { id: 'peer2', url: 'btp+ws://...', authToken: 'token2' },
560
+ * ]);
561
+ *
562
+ * results.forEach(result => {
563
+ * if (result.success) {
564
+ * console.log(`Added peer: ${result.peerId}`);
565
+ * } else {
566
+ * console.error(`Failed to add ${result.peerId}:`, result.error);
567
+ * }
568
+ * });
569
+ * ```
570
+ */
571
+ addPeers(configs: {
572
+ id: string;
573
+ url: string;
574
+ authToken: string;
575
+ routes?: {
576
+ prefix: string;
577
+ priority?: number;
578
+ }[];
579
+ settlement?: {
580
+ preference: string;
581
+ evmAddress?: string;
582
+ tokenAddress?: string;
583
+ tokenNetworkAddress?: string;
584
+ chainId?: number;
585
+ channelId?: string;
586
+ initialDeposit?: string;
587
+ };
588
+ }[]): Promise<PeerOperationResult[]>;
589
+ /**
590
+ * Remove multiple peers in parallel.
591
+ *
592
+ * Uses Promise.allSettled() to execute peer removals concurrently,
593
+ * returning results for each operation regardless of individual failures.
594
+ *
595
+ * @param peerIds - Array of peer IDs to remove
596
+ * @returns Array of results indicating success/failure for each peer
597
+ *
598
+ * @example
599
+ * ```typescript
600
+ * const results = await admin.removePeers(['peer1', 'peer2', 'peer3']);
601
+ *
602
+ * const succeeded = results.filter(r => r.success).length;
603
+ * console.log(`Removed ${succeeded}/${results.length} peers`);
604
+ * ```
605
+ */
606
+ removePeers(peerIds: string[]): Promise<PeerOperationResult[]>;
607
+ /**
608
+ * Send HTTP POST request to add a peer.
609
+ * Separated for retry logic wrapping.
610
+ */
611
+ private sendAddPeerRequest;
612
+ /**
613
+ * Send HTTP DELETE request to remove a peer.
614
+ * Separated for retry logic wrapping.
615
+ */
616
+ private sendRemovePeerRequest;
617
+ /**
618
+ * Handle network errors from HTTP requests.
619
+ *
620
+ * Converts connection failures, timeouts, and unknown errors to NetworkError.
621
+ * Re-throws existing ToonClientError instances.
622
+ *
623
+ * @param error - Error thrown by HTTP client
624
+ * @param url - Request URL (for error messages)
625
+ * @param operation - Operation name (for error messages)
626
+ * @throws {NetworkError} Network connection or timeout error
627
+ */
628
+ private handleNetworkError;
629
+ /**
630
+ * Handle HTTP error responses from the admin API.
631
+ *
632
+ * Converts HTTP status codes to appropriate error types.
633
+ *
634
+ * @param response - HTTP response from admin API
635
+ * @param endpoint - Endpoint being called (for error messages)
636
+ * @param peerId - Peer ID (for error messages)
637
+ * @throws {UnauthorizedError} 401 Unauthorized
638
+ * @throws {PeerNotFoundError} 404 Not Found
639
+ * @throws {PeerAlreadyExistsError} 409 Conflict
640
+ * @throws {ConnectorError} 5xx Server Error
641
+ */
642
+ private handleErrorResponse;
643
+ }
644
+
645
+ /**
646
+ * EVM claim message for BTP protocol data.
647
+ * Matches @toon-protocol/connector's EVMClaimMessage interface.
648
+ */
649
+ interface EVMClaimMessage {
650
+ blockchain: 'evm';
651
+ senderId: string;
652
+ channelId: string;
653
+ nonce: number;
654
+ transferredAmount: string;
655
+ lockedAmount: string;
656
+ locksRoot: string;
657
+ signature: string;
658
+ signerAddress: string;
659
+ }
660
+ /**
661
+ * EVM signer for EIP-712 balance proofs and on-chain transactions.
662
+ *
663
+ * Encapsulates the private key — no getPrivateKey() method is exposed.
664
+ */
665
+ declare class EvmSigner {
666
+ private readonly _account;
667
+ /**
668
+ * @param privateKey - EVM private key as hex string (with or without 0x prefix) or Uint8Array
669
+ */
670
+ constructor(privateKey: string | Uint8Array);
671
+ /** Derived 0x EVM address */
672
+ get address(): string;
673
+ /** Viem PrivateKeyAccount — usable with walletClient for on-chain transactions */
674
+ get account(): PrivateKeyAccount;
675
+ /**
676
+ * Signs a balance proof using EIP-712 typed data.
677
+ *
678
+ * @param params - Balance proof parameters plus chain context
679
+ * @returns Signed balance proof with signature
680
+ */
681
+ signBalanceProof(params: BalanceProofParams & {
682
+ chainId: number;
683
+ tokenNetworkAddress: string;
684
+ }): Promise<SignedBalanceProof>;
685
+ /**
686
+ * Builds an EVMClaimMessage from a signed balance proof.
687
+ * Static so it can be called without an EvmSigner instance.
688
+ *
689
+ * @param proof - Signed balance proof
690
+ * @param senderId - Nostr pubkey or identifier of the sender
691
+ * @returns EVMClaimMessage compatible with BTP_CLAIM_PROTOCOL
692
+ */
693
+ static buildClaimMessage(proof: SignedBalanceProof, senderId: string): EVMClaimMessage;
694
+ }
695
+
696
+ /** Pino-compatible logger interface */
697
+ interface ConsoleLogger {
698
+ level: string;
699
+ silent: (...args: unknown[]) => void;
700
+ info: typeof console.info;
701
+ warn: typeof console.warn;
702
+ error: typeof console.error;
703
+ debug: typeof console.debug;
704
+ trace: typeof console.debug;
705
+ fatal: typeof console.error;
706
+ child: () => ConsoleLogger;
707
+ }
708
+ interface BtpRuntimeClientConfig {
709
+ btpUrl: string;
710
+ peerId: string;
711
+ authToken: string;
712
+ logger?: ConsoleLogger;
713
+ /** Max reconnection attempts on send failure (default: 3) */
714
+ maxRetries?: number;
715
+ /** Delay between reconnection attempts in ms (default: 1000) */
716
+ retryDelay?: number;
717
+ }
718
+ /**
719
+ * BTP transport implementing IlpClient.
720
+ * Wraps BTPClient from @toon-protocol/connector with auto-reconnect on connection loss.
721
+ */
722
+ declare class BtpRuntimeClient implements IlpClient {
723
+ private btpClient;
724
+ private readonly config;
725
+ private _isConnected;
726
+ private readonly logger;
727
+ constructor(config: BtpRuntimeClientConfig);
728
+ /**
729
+ * Connects to the BTP peer via WebSocket.
730
+ */
731
+ connect(): Promise<void>;
732
+ /**
733
+ * Attempts to reconnect by creating a fresh BTPClient and connecting.
734
+ */
735
+ reconnect(): Promise<void>;
736
+ /**
737
+ * Disconnects from the BTP peer.
738
+ */
739
+ disconnect(): Promise<void>;
740
+ get isConnected(): boolean;
741
+ /**
742
+ * Sends an ILP packet via BTP with auto-reconnect on connection errors.
743
+ * Satisfies IlpClient interface.
744
+ */
745
+ sendIlpPacket(params: {
746
+ destination: string;
747
+ amount: string;
748
+ data: string;
749
+ timeout?: number;
750
+ }): Promise<IlpSendResult>;
751
+ /**
752
+ * Sends a balance proof claim via BTP protocol data, then sends an ILP packet.
753
+ * Auto-reconnects on connection errors.
754
+ */
755
+ sendIlpPacketWithClaim(params: {
756
+ destination: string;
757
+ amount: string;
758
+ data: string;
759
+ timeout?: number;
760
+ }, claim: EVMClaimMessage): Promise<IlpSendResult>;
761
+ /**
762
+ * Single-attempt ILP packet send. Reconnects if not connected.
763
+ */
764
+ private _sendIlpPacketOnce;
765
+ /**
766
+ * Single-attempt claim + ILP packet send. Reconnects if not connected.
767
+ */
768
+ private _sendIlpPacketWithClaimOnce;
769
+ }
770
+
771
+ interface OnChainChannelClientConfig {
772
+ evmSigner: EvmSigner;
773
+ chainRpcUrls: Record<string, string>;
774
+ }
775
+ /**
776
+ * Implements ConnectorChannelClient using viem for direct on-chain
777
+ * interaction with TokenNetwork smart contract.
778
+ *
779
+ * Fully non-custodial — the client deposits its own funds on-chain.
780
+ */
781
+ declare class OnChainChannelClient implements ConnectorChannelClient {
782
+ private readonly evmSigner;
783
+ private readonly chainRpcUrls;
784
+ private readonly channelContext;
785
+ constructor(config: OnChainChannelClientConfig);
786
+ /**
787
+ * Parse chain identifier to extract chainId.
788
+ * Format: "evm:{network}:{chainId}" e.g., "evm:anvil:31337"
789
+ */
790
+ private parseChainId;
791
+ /**
792
+ * Create viem clients for a given chain.
793
+ */
794
+ private createClients;
795
+ /**
796
+ * Opens a new payment channel on-chain.
797
+ *
798
+ * 1. Approve token spend if needed
799
+ * 2. Call TokenNetwork.openChannel()
800
+ * 3. Extract channelId from ChannelOpened event
801
+ * 4. Deposit initial funds if specified
802
+ */
803
+ openChannel(params: OpenChannelParams): Promise<OpenChannelResult>;
804
+ /**
805
+ * Gets the current state of a payment channel from on-chain data.
806
+ */
807
+ getChannelState(channelId: string): Promise<ChannelState>;
808
+ }
809
+
810
+ interface ChannelStoreEntry {
811
+ nonce: number;
812
+ cumulativeAmount: bigint;
813
+ }
814
+ /**
815
+ * Persistence interface for payment channel nonce/amount state.
816
+ */
817
+ interface ChannelStore {
818
+ save(channelId: string, tracking: ChannelStoreEntry): void;
819
+ load(channelId: string): ChannelStoreEntry | undefined;
820
+ list(): string[];
821
+ delete(channelId: string): void;
822
+ }
823
+
824
+ /**
825
+ * Local nonce tracking and claim signing.
826
+ *
827
+ * Does NOT make any network calls — it only manages state
828
+ * and delegates signing to EvmSigner.
829
+ */
830
+ declare class ChannelManager {
831
+ private readonly evmSigner;
832
+ private readonly channels;
833
+ private readonly store?;
834
+ constructor(evmSigner: EvmSigner, store?: ChannelStore);
835
+ /**
836
+ * Start tracking a channel.
837
+ * Called after bootstrap returns a channelId.
838
+ *
839
+ * @param channelId - Payment channel identifier
840
+ * @param initialNonce - Starting nonce (default: 0)
841
+ * @param initialAmount - Starting cumulative amount (default: 0n)
842
+ */
843
+ trackChannel(channelId: string, initialNonce?: number, initialAmount?: bigint): void;
844
+ /**
845
+ * Signs a balance proof for the given channel.
846
+ * Auto-increments nonce and adds to cumulative amount.
847
+ *
848
+ * @param channelId - Payment channel identifier
849
+ * @param additionalAmount - Amount to add to cumulative transferred amount
850
+ * @returns Signed balance proof
851
+ * @throws Error if channel is not being tracked
852
+ */
853
+ signBalanceProof(channelId: string, additionalAmount: bigint): Promise<SignedBalanceProof>;
854
+ /**
855
+ * Gets the current nonce for a tracked channel.
856
+ */
857
+ getNonce(channelId: string): number;
858
+ /**
859
+ * Gets the cumulative transferred amount for a tracked channel.
860
+ */
861
+ getCumulativeAmount(channelId: string): bigint;
862
+ /**
863
+ * Gets all tracked channel IDs.
864
+ */
865
+ getTrackedChannels(): string[];
866
+ /**
867
+ * Returns true if the channel is being tracked.
868
+ */
869
+ isTracking(channelId: string): boolean;
870
+ }
871
+
872
+ /**
873
+ * Configuration options for retry behavior with exponential backoff.
874
+ */
875
+ interface RetryOptions {
876
+ /** Maximum number of retry attempts (default: 3) */
877
+ maxRetries: number;
878
+ /** Initial delay in milliseconds between retries (default: 1000) */
879
+ retryDelay: number;
880
+ /** Use exponential backoff for delays (default: true) */
881
+ exponentialBackoff?: boolean;
882
+ /** Maximum delay cap in milliseconds (default: 30000) */
883
+ maxDelay?: number;
884
+ /** Custom predicate to determine if an error should trigger a retry */
885
+ shouldRetry?: (error: Error) => boolean;
886
+ }
887
+ /**
888
+ * Executes an async operation with retry logic and exponential backoff.
889
+ *
890
+ * @param operation - The async function to execute
891
+ * @param options - Retry configuration options
892
+ * @returns The result of the successful operation
893
+ * @throws The last error if all retries are exhausted
894
+ *
895
+ * @example
896
+ * ```typescript
897
+ * const result = await withRetry(
898
+ * async () => fetchData(),
899
+ * {
900
+ * maxRetries: 3,
901
+ * retryDelay: 1000,
902
+ * shouldRetry: (err) => err.name === 'NetworkError'
903
+ * }
904
+ * );
905
+ * ```
906
+ */
907
+ declare function withRetry<T>(operation: () => Promise<T>, options: RetryOptions): Promise<T>;
908
+
909
+ /**
910
+ * Settlement info produced by buildSettlementInfo().
911
+ * Extends the core SettlementConfig shape with ilpAddress for client use.
912
+ */
913
+ interface ClientSettlementInfo {
914
+ ilpAddress?: string;
915
+ supportedChains?: string[];
916
+ settlementAddresses?: Record<string, string>;
917
+ preferredTokens?: Record<string, string>;
918
+ tokenNetworks?: Record<string, string>;
919
+ }
920
+ /**
921
+ * Validates ToonClient configuration.
922
+ *
923
+ * This story implements HTTP mode only. Embedded mode validation will be added in a future epic.
924
+ *
925
+ * @throws {ValidationError} If configuration is invalid
926
+ */
927
+ declare function validateConfig(config: ToonClientConfig): void;
928
+ /**
929
+ * The resolved config type after defaults are applied.
930
+ * secretKey is guaranteed to be present (auto-generated if omitted).
931
+ */
932
+ type ResolvedConfig = Required<Omit<ToonClientConfig, 'connector' | 'evmPrivateKey' | 'supportedChains' | 'settlementAddresses' | 'preferredTokens' | 'tokenNetworks' | 'btpUrl' | 'btpAuthToken' | 'btpPeerId' | 'chainRpcUrls' | 'initialDeposit' | 'settlementTimeout' | 'channelStorePath' | 'knownPeers' | 'destinationAddress'>> & {
933
+ connector?: unknown;
934
+ /** Always present after applyDefaults() — derived from secretKey if not explicitly provided */
935
+ evmPrivateKey: string | Uint8Array;
936
+ supportedChains?: string[];
937
+ settlementAddresses?: Record<string, string>;
938
+ preferredTokens?: Record<string, string>;
939
+ tokenNetworks?: Record<string, string>;
940
+ btpUrl?: string;
941
+ btpAuthToken?: string;
942
+ btpPeerId?: string;
943
+ chainRpcUrls?: Record<string, string>;
944
+ initialDeposit?: string;
945
+ settlementTimeout?: number;
946
+ channelStorePath?: string;
947
+ knownPeers?: {
948
+ pubkey: string;
949
+ relayUrl: string;
950
+ btpEndpoint?: string;
951
+ }[];
952
+ destinationAddress: string;
953
+ };
954
+ /**
955
+ * Applies default values to optional configuration fields.
956
+ * Auto-generates a Nostr keypair when secretKey is omitted.
957
+ * Derives btpUrl from connectorUrl when not provided.
958
+ */
959
+ declare function applyDefaults(config: ToonClientConfig): ResolvedConfig;
960
+ /**
961
+ * Builds SettlementConfig from client config.
962
+ * Returns undefined if no settlement-related config is present.
963
+ */
964
+ declare function buildSettlementInfo(config: ToonClientConfig): ClientSettlementInfo | undefined;
965
+
966
+ export { type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, ChannelManager, ConnectorError, type EVMClaimMessage, EvmSigner, HttpConnectorAdmin, type HttpConnectorAdminConfig, HttpRuntimeClient, type HttpRuntimeClientConfig, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type PublishEventResult, type RetryOptions, type SignedBalanceProof, ToonClient, type ToonClientConfig, ToonClientError, type ToonStartResult, ValidationError, applyDefaults, buildSettlementInfo, validateConfig, withRetry };