@openzeppelin/relayer-plugin-channels 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +79 -43
  2. package/dist/client/channels-client.d.ts +1 -1
  3. package/dist/client/channels-client.d.ts.map +1 -1
  4. package/dist/client/channels-client.js +20 -21
  5. package/dist/client/errors.d.ts +1 -1
  6. package/dist/client/errors.js +12 -9
  7. package/dist/client/index.d.ts +3 -3
  8. package/dist/client/index.d.ts.map +1 -1
  9. package/dist/client/types.d.ts +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/plugin/build.d.ts +1 -1
  12. package/dist/plugin/build.d.ts.map +1 -1
  13. package/dist/plugin/build.js +11 -17
  14. package/dist/plugin/config.d.ts +4 -2
  15. package/dist/plugin/config.d.ts.map +1 -1
  16. package/dist/plugin/config.js +43 -13
  17. package/dist/plugin/constants.d.ts +1 -2
  18. package/dist/plugin/constants.d.ts.map +1 -1
  19. package/dist/plugin/constants.js +2 -3
  20. package/dist/plugin/fee-tracking.d.ts +2 -2
  21. package/dist/plugin/fee-tracking.d.ts.map +1 -1
  22. package/dist/plugin/fee-tracking.js +4 -9
  23. package/dist/plugin/fee.d.ts +16 -5
  24. package/dist/plugin/fee.d.ts.map +1 -1
  25. package/dist/plugin/fee.js +52 -14
  26. package/dist/plugin/handler.d.ts +10 -1
  27. package/dist/plugin/handler.d.ts.map +1 -1
  28. package/dist/plugin/handler.js +87 -52
  29. package/dist/plugin/index.d.ts +1 -1
  30. package/dist/plugin/management.d.ts +1 -1
  31. package/dist/plugin/management.d.ts.map +1 -1
  32. package/dist/plugin/management.js +53 -79
  33. package/dist/plugin/pool.d.ts +8 -3
  34. package/dist/plugin/pool.d.ts.map +1 -1
  35. package/dist/plugin/pool.js +45 -17
  36. package/dist/plugin/simulation.d.ts +4 -2
  37. package/dist/plugin/simulation.d.ts.map +1 -1
  38. package/dist/plugin/simulation.js +41 -33
  39. package/dist/plugin/submit.d.ts +7 -5
  40. package/dist/plugin/submit.d.ts.map +1 -1
  41. package/dist/plugin/submit.js +23 -13
  42. package/dist/plugin/tx.d.ts +1 -1
  43. package/dist/plugin/tx.d.ts.map +1 -1
  44. package/dist/plugin/tx.js +6 -8
  45. package/dist/plugin/types.d.ts +4 -4
  46. package/dist/plugin/types.d.ts.map +1 -1
  47. package/dist/plugin/validation.d.ts +1 -1
  48. package/dist/plugin/validation.d.ts.map +1 -1
  49. package/dist/plugin/validation.js +26 -27
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -16,6 +16,7 @@ A plugin for OpenZeppelin Relayer that enables parallel transaction submission o
16
16
  - [Development](#development)
17
17
  - [Overview](#overview)
18
18
  - [Architecture](#architecture)
19
+ - [Contract Capacity Limits](#contract-capacity-limits)
19
20
  - [Management API](#management-api)
20
21
  - [List Channel Accounts](#list-channel-accounts)
21
22
  - [Set Channel Accounts](#set-channel-accounts)
@@ -109,7 +110,7 @@ mkdir -p plugins/channels
109
110
  `plugins/channels/index.ts`
110
111
 
111
112
  ```ts
112
- export { handler } from "@openzeppelin/relayer-plugin-channels";
113
+ export { handler } from '@openzeppelin/relayer-plugin-channels';
113
114
  ```
114
115
 
115
116
  ### Configure the Relayer
@@ -224,6 +225,10 @@ export LOCK_TTL_SECONDS=10 # default: 30, min: 3, max: 30
224
225
  export FEE_LIMIT=1000000 # Default max fee per API key in stroops (disabled if not set)
225
226
  export FEE_RESET_PERIOD_SECONDS=86400 # Reset fee consumption every N seconds (e.g., 86400 = 24 hours)
226
227
  export API_KEY_HEADER="x-api-key" # Header name to extract API key (default: x-api-key)
228
+
229
+ # Contract capacity limits (optional)
230
+ export LIMITED_CONTRACTS="CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA" # Comma-separated contract addresses
231
+ export CONTRACT_CAPACITY_RATIO=0.8 # Max ratio of pool for limited contracts (default: 0.8 = 80%)
227
232
  ```
228
233
 
229
234
  Your Relayer should now contain:
@@ -291,6 +296,41 @@ The Channels plugin accepts Soroban operations and handles all the complexity of
291
296
  - **Channel Accounts**: Provide unique sequence numbers for parallel transaction submission
292
297
  - The channel account is the transaction source and signer; the fund account wraps it in a fee bump
293
298
 
299
+ ## Contract Capacity Limits
300
+
301
+ High-volume contracts can monopolize the channel pool, starving other traffic. Contract capacity limits allow you to reserve a portion of the pool for non-limited contracts.
302
+
303
+ ### Configuration
304
+
305
+ ```bash
306
+ # Comma-separated list of contract addresses to limit (case-insensitive)
307
+ export LIMITED_CONTRACTS="CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA,CABC123..."
308
+
309
+ # Maximum ratio of pool that limited contracts can use (default: 0.8)
310
+ export CONTRACT_CAPACITY_RATIO=0.8
311
+ ```
312
+
313
+ ### How It Works
314
+
315
+ - **Limited contracts** can only acquire from a deterministic subset of channels (e.g., 80% of pool)
316
+ - **Unlisted contracts** have full access to all channels
317
+ - The subset is selected deterministically using a hash-based partition, ensuring stable channel assignment
318
+ - Minimum 1 channel is always guaranteed, even at very low ratios
319
+
320
+ ### Example
321
+
322
+ With 10 channel accounts and `CONTRACT_CAPACITY_RATIO=0.8`:
323
+
324
+ - Limited contracts can use up to 8 channels (`floor(10 * 0.8)`)
325
+ - 2 channels are always reserved for non-limited traffic
326
+ - If all 8 limited slots are in use, limited contracts get `POOL_CAPACITY` error while non-limited contracts can still acquire
327
+
328
+ ### Notes
329
+
330
+ - Contract IDs are matched case-insensitively (normalized to uppercase internally)
331
+ - If contract ID cannot be extracted from the request (non-`invokeContract` operations), no limit is applied
332
+ - Zero runtime overhead for unlimited contracts
333
+
294
334
  ## Management API
295
335
 
296
336
  The Channels plugin provides a management API to dynamically configure channel accounts. This API requires authentication via the `PLUGIN_ADMIN_SECRET` environment variable.
@@ -492,20 +532,20 @@ pnpm add @openzeppelin/relayer-plugin-channels
492
532
  ### Quick Start
493
533
 
494
534
  ```typescript
495
- import { ChannelsClient } from "@openzeppelin/relayer-plugin-channels";
535
+ import { ChannelsClient } from '@openzeppelin/relayer-plugin-channels';
496
536
 
497
537
  // Connecting to OpenZeppelin's managed Channels service
498
538
  const client = new ChannelsClient({
499
- baseUrl: "https://channels.openzeppelin.com",
500
- apiKey: "your-api-key",
539
+ baseUrl: 'https://channels.openzeppelin.com',
540
+ apiKey: 'your-api-key',
501
541
  });
502
542
 
503
543
  // Connecting to your own Relayer with Channels plugin
504
544
  const relayerClient = new ChannelsClient({
505
- baseUrl: "http://localhost:8080",
506
- pluginId: "channels",
507
- apiKey: "your-relayer-api-key",
508
- adminSecret: "your-admin-secret", // Optional: Required for management operations
545
+ baseUrl: 'http://localhost:8080',
546
+ pluginId: 'channels',
547
+ apiKey: 'your-relayer-api-key',
548
+ adminSecret: 'your-admin-secret', // Optional: Required for management operations
509
549
  });
510
550
  ```
511
551
 
@@ -518,14 +558,14 @@ When connecting to OpenZeppelin's managed Channels service (which runs behind Cl
518
558
  ```typescript
519
559
  // Mainnet
520
560
  const client = new ChannelsClient({
521
- baseUrl: "https://channels.openzeppelin.com",
522
- apiKey: "your-api-key",
561
+ baseUrl: 'https://channels.openzeppelin.com',
562
+ apiKey: 'your-api-key',
523
563
  });
524
564
 
525
565
  // Testnet
526
566
  const testnetClient = new ChannelsClient({
527
- baseUrl: "https://channels.openzeppelin.com/testnet",
528
- apiKey: "your-api-key",
567
+ baseUrl: 'https://channels.openzeppelin.com/testnet',
568
+ apiKey: 'your-api-key',
529
569
  });
530
570
  ```
531
571
 
@@ -540,10 +580,10 @@ When connecting directly to your own OpenZeppelin Relayer instance, include the
540
580
 
541
581
  ```typescript
542
582
  const client = new ChannelsClient({
543
- baseUrl: "http://localhost:8080",
544
- pluginId: "channels",
545
- apiKey: "your-relayer-api-key",
546
- adminSecret: "your-admin-secret", // Optional: Required for management operations
583
+ baseUrl: 'http://localhost:8080',
584
+ pluginId: 'channels',
585
+ apiKey: 'your-relayer-api-key',
586
+ adminSecret: 'your-admin-secret', // Optional: Required for management operations
547
587
  });
548
588
  ```
549
589
 
@@ -556,7 +596,7 @@ The client automatically routes requests appropriately based on whether `pluginI
556
596
  ```typescript
557
597
  // Submit a complete, signed transaction
558
598
  const result = await client.submitTransaction({
559
- xdr: "AAAAAgAAAAC...", // Complete transaction envelope XDR
599
+ xdr: 'AAAAAgAAAAC...', // Complete transaction envelope XDR
560
600
  });
561
601
 
562
602
  console.log(result.hash); // Transaction hash
@@ -569,8 +609,8 @@ console.log(result.transactionId); // Relayer transaction ID
569
609
  ```typescript
570
610
  // Submit func+auth (uses channel accounts and simulation)
571
611
  const result = await client.submitSorobanTransaction({
572
- func: "AAAABAAAAAEAAAAGc3ltYm9s...", // Host function XDR (base64)
573
- auth: ["AAAACAAAAAEAAAA..."], // Auth entry XDRs (base64)
612
+ func: 'AAAABAAAAAEAAAAGc3ltYm9s...', // Host function XDR (base64)
613
+ auth: ['AAAACAAAAAEAAAA...'], // Auth entry XDRs (base64)
574
614
  });
575
615
 
576
616
  console.log(result.hash);
@@ -581,10 +621,10 @@ console.log(result.hash);
581
621
  ```typescript
582
622
  // Initialize client with admin secret
583
623
  const adminClient = new ChannelsClient({
584
- baseUrl: "http://localhost:8080",
585
- apiKey: "your-api-key",
586
- pluginId: "channels",
587
- adminSecret: "your-admin-secret", // Required for management operations
624
+ baseUrl: 'http://localhost:8080',
625
+ apiKey: 'your-api-key',
626
+ pluginId: 'channels',
627
+ adminSecret: 'your-admin-secret', // Required for management operations
588
628
  });
589
629
 
590
630
  // List configured channel accounts
@@ -596,11 +636,7 @@ console.log(accounts.relayerIds); // ['channel-001', 'channel-002', ...]
596
636
 
597
637
  ```typescript
598
638
  // Configure channel accounts (requires adminSecret)
599
- const result = await adminClient.setChannelAccounts([
600
- "channel-001",
601
- "channel-002",
602
- "channel-003",
603
- ]);
639
+ const result = await adminClient.setChannelAccounts(['channel-001', 'channel-002', 'channel-003']);
604
640
 
605
641
  console.log(result.ok); // true
606
642
  console.log(result.appliedRelayerIds); // ['channel-001', 'channel-002', 'channel-003']
@@ -610,7 +646,7 @@ console.log(result.appliedRelayerIds); // ['channel-001', 'channel-002', 'channe
610
646
 
611
647
  ```typescript
612
648
  // Query fee consumption for an API key (requires adminSecret)
613
- const usage = await adminClient.getFeeUsage("client-api-key");
649
+ const usage = await adminClient.getFeeUsage('client-api-key');
614
650
 
615
651
  console.log(usage.consumed); // 500000 (stroops)
616
652
  console.log(usage.limit); // 1000000 (effective limit)
@@ -623,7 +659,7 @@ console.log(usage.periodEndsAt); // '2024-01-16T00:00:00.000Z'
623
659
 
624
660
  ```typescript
625
661
  // Query fee limit configuration for an API key (requires adminSecret)
626
- const limitInfo = await adminClient.getFeeLimit("client-api-key");
662
+ const limitInfo = await adminClient.getFeeLimit('client-api-key');
627
663
 
628
664
  console.log(limitInfo.limit); // 500000 (custom limit if set, otherwise default)
629
665
  ```
@@ -632,7 +668,7 @@ console.log(limitInfo.limit); // 500000 (custom limit if set, otherwise default)
632
668
 
633
669
  ```typescript
634
670
  // Set a custom fee limit for an API key (requires adminSecret)
635
- const result = await adminClient.setFeeLimit("client-api-key", 500000);
671
+ const result = await adminClient.setFeeLimit('client-api-key', 500000);
636
672
 
637
673
  console.log(result.ok); // true
638
674
  console.log(result.limit); // 500000
@@ -642,7 +678,7 @@ console.log(result.limit); // 500000
642
678
 
643
679
  ```typescript
644
680
  // Remove custom fee limit, revert to default (requires adminSecret)
645
- const result = await adminClient.deleteFeeLimit("client-api-key");
681
+ const result = await adminClient.deleteFeeLimit('client-api-key');
646
682
 
647
683
  console.log(result.ok); // true
648
684
  ```
@@ -656,22 +692,22 @@ import {
656
692
  PluginTransportError,
657
693
  PluginExecutionError,
658
694
  PluginUnexpectedError,
659
- } from "@openzeppelin/relayer-plugin-channels";
695
+ } from '@openzeppelin/relayer-plugin-channels';
660
696
 
661
697
  try {
662
- const result = await client.submitTransaction({ xdr: "..." });
698
+ const result = await client.submitTransaction({ xdr: '...' });
663
699
  } catch (error) {
664
700
  if (error instanceof PluginTransportError) {
665
701
  // Network/HTTP failures (connection refused, timeout, 500/502/503)
666
- console.error("Transport error:", error.message);
667
- console.error("Status code:", error.statusCode);
702
+ console.error('Transport error:', error.message);
703
+ console.error('Status code:', error.statusCode);
668
704
  } else if (error instanceof PluginExecutionError) {
669
705
  // Plugin rejected the request (validation, business logic, on-chain failure)
670
- console.error("Execution error:", error.message);
671
- console.error("Details:", error.errorDetails);
706
+ console.error('Execution error:', error.message);
707
+ console.error('Details:', error.errorDetails);
672
708
  } else if (error instanceof PluginUnexpectedError) {
673
709
  // Client-side parsing/validation errors
674
- console.error("Unexpected error:", error.message);
710
+ console.error('Unexpected error:', error.message);
675
711
  }
676
712
  }
677
713
  ```
@@ -681,12 +717,12 @@ try {
681
717
  Responses include optional metadata (logs and traces) when the plugin is configured with `emit_logs` and `emit_traces`:
682
718
 
683
719
  ```typescript
684
- const result = await client.submitTransaction({ xdr: "..." });
720
+ const result = await client.submitTransaction({ xdr: '...' });
685
721
 
686
722
  // Access metadata if available
687
723
  if (result.metadata) {
688
- console.log("Logs:", result.metadata.logs);
689
- console.log("Traces:", result.metadata.traces);
724
+ console.log('Logs:', result.metadata.logs);
725
+ console.log('Traces:', result.metadata.traces);
690
726
  }
691
727
  ```
692
728
 
@@ -705,7 +741,7 @@ import type {
705
741
  GetFeeLimitResponse,
706
742
  SetFeeLimitResponse,
707
743
  DeleteFeeLimitResponse,
708
- } from "@openzeppelin/relayer-plugin-channels";
744
+ } from '@openzeppelin/relayer-plugin-channels';
709
745
  ```
710
746
 
711
747
  ### Configuration Options
@@ -1,4 +1,4 @@
1
- import type { ChannelsClientConfig, ChannelsXdrRequest, ChannelsFuncAuthRequest, ChannelsTransactionResponse, ListChannelAccountsResponse, SetChannelAccountsResponse, GetFeeUsageResponse, GetFeeLimitResponse, SetFeeLimitResponse, DeleteFeeLimitResponse } from "./types";
1
+ import type { ChannelsClientConfig, ChannelsXdrRequest, ChannelsFuncAuthRequest, ChannelsTransactionResponse, ListChannelAccountsResponse, SetChannelAccountsResponse, GetFeeUsageResponse, GetFeeLimitResponse, SetFeeLimitResponse, DeleteFeeLimitResponse } from './types';
2
2
  /**
3
3
  * Client for interacting with the Channels plugin
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"channels-client.d.ts","sourceRoot":"","sources":["../../src/client/channels-client.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EAEvB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAgB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;gBAEvB,MAAM,EAAE,oBAAoB;IAiCxC;;;;;;;;;;;;;OAaG;IACG,iBAAiB,CACrB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,2BAA2B,CAAC;IAIvC;;;;;;;;;;;;;;;OAeG;IACG,wBAAwB,CAC5B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,2BAA2B,CAAC;IAIvC;;;;;;;;;;;;OAYG;IACG,mBAAmB,IAAI,OAAO,CAAC,2BAA2B,CAAC;IASjE;;;;;;;;;;;;;;;OAeG;IACG,kBAAkB,CACtB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,0BAA0B,CAAC;IAUtC;;;;;;;;;;;;;OAaG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU/D;;;;;;;;;OASG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU/D;;;;;;;;;;OAUG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,mBAAmB,CAAC;IAW/B;;;;;;;;;OASG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAUrE;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;;;;;;OAOG;IAEH,OAAO,CAAC,eAAe;IAoBvB;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAUrB;;;;;;;;OAQG;YACW,IAAI;IA6BlB;;;;;;OAMG;YACW,QAAQ;CAYvB"}
1
+ {"version":3,"file":"channels-client.d.ts","sourceRoot":"","sources":["../../src/client/channels-client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EAEvB,MAAM,SAAS,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAgB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;gBAEvB,MAAM,EAAE,oBAAoB;IAiCxC;;;;;;;;;;;;;OAaG;IACG,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAI1F;;;;;;;;;;;;;;;OAeG;IACG,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAItG;;;;;;;;;;;;OAYG;IACG,mBAAmB,IAAI,OAAO,CAAC,2BAA2B,CAAC;IASjE;;;;;;;;;;;;;;;OAeG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAUnF;;;;;;;;;;;;;OAaG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU/D;;;;;;;;;OASG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU/D;;;;;;;;;;OAUG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAW9E;;;;;;;;;OASG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAUrE;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;;;;;;OAOG;IAEH,OAAO,CAAC,eAAe;IAgBvB;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAUrB;;;;;;;;OAQG;YACW,IAAI;IAwBlB;;;;;;OAMG;YACW,QAAQ;CASvB"}
@@ -31,9 +31,9 @@ class ChannelsClient {
31
31
  constructor(config) {
32
32
  this.adminSecret = config.adminSecret;
33
33
  // Route through Relayer plugin system if pluginId provided, otherwise connect directly
34
- if ("pluginId" in config && config.pluginId) {
34
+ if ('pluginId' in config && config.pluginId) {
35
35
  this.pluginId = config.pluginId;
36
- const apiKeyHeader = config.apiKeyHeader || "x-api-key";
36
+ const apiKeyHeader = config.apiKeyHeader || 'x-api-key';
37
37
  const relayerConfig = new relayer_sdk_1.Configuration({
38
38
  basePath: config.baseUrl,
39
39
  accessToken: config.apiKey,
@@ -44,14 +44,14 @@ class ChannelsClient {
44
44
  this.pluginsApi = new relayer_sdk_1.PluginsApi(relayerConfig);
45
45
  }
46
46
  else {
47
- if (!("baseUrl" in config) || !config.baseUrl) {
48
- throw new Error("baseUrl is required when pluginId is not provided");
47
+ if (!('baseUrl' in config) || !config.baseUrl) {
48
+ throw new Error('baseUrl is required when pluginId is not provided');
49
49
  }
50
50
  this.axiosClient = axios_1.default.create({
51
51
  baseURL: config.baseUrl,
52
52
  timeout: config.timeout || 30000,
53
53
  headers: {
54
- "Content-Type": "application/json",
54
+ 'Content-Type': 'application/json',
55
55
  Authorization: `Bearer ${config.apiKey}`,
56
56
  },
57
57
  });
@@ -109,7 +109,7 @@ class ChannelsClient {
109
109
  async listChannelAccounts() {
110
110
  return this.call({
111
111
  management: {
112
- action: "listChannelAccounts",
112
+ action: 'listChannelAccounts',
113
113
  adminSecret: this.requireAdminSecret(),
114
114
  },
115
115
  });
@@ -133,7 +133,7 @@ class ChannelsClient {
133
133
  async setChannelAccounts(relayerIds) {
134
134
  return this.call({
135
135
  management: {
136
- action: "setChannelAccounts",
136
+ action: 'setChannelAccounts',
137
137
  adminSecret: this.requireAdminSecret(),
138
138
  relayerIds,
139
139
  },
@@ -156,7 +156,7 @@ class ChannelsClient {
156
156
  async getFeeUsage(apiKey) {
157
157
  return this.call({
158
158
  management: {
159
- action: "getFeeUsage",
159
+ action: 'getFeeUsage',
160
160
  adminSecret: this.requireAdminSecret(),
161
161
  apiKey,
162
162
  },
@@ -175,7 +175,7 @@ class ChannelsClient {
175
175
  async getFeeLimit(apiKey) {
176
176
  return this.call({
177
177
  management: {
178
- action: "getFeeLimit",
178
+ action: 'getFeeLimit',
179
179
  adminSecret: this.requireAdminSecret(),
180
180
  apiKey,
181
181
  },
@@ -195,7 +195,7 @@ class ChannelsClient {
195
195
  async setFeeLimit(apiKey, limit) {
196
196
  return this.call({
197
197
  management: {
198
- action: "setFeeLimit",
198
+ action: 'setFeeLimit',
199
199
  adminSecret: this.requireAdminSecret(),
200
200
  apiKey,
201
201
  limit,
@@ -215,7 +215,7 @@ class ChannelsClient {
215
215
  async deleteFeeLimit(apiKey) {
216
216
  return this.call({
217
217
  management: {
218
- action: "deleteFeeLimit",
218
+ action: 'deleteFeeLimit',
219
219
  adminSecret: this.requireAdminSecret(),
220
220
  apiKey,
221
221
  },
@@ -229,7 +229,7 @@ class ChannelsClient {
229
229
  */
230
230
  requireAdminSecret() {
231
231
  if (!this.adminSecret) {
232
- throw new Error("adminSecret required for management operations. Provide it in client config.");
232
+ throw new Error('adminSecret required for management operations. Provide it in client config.');
233
233
  }
234
234
  return this.adminSecret;
235
235
  }
@@ -262,12 +262,12 @@ class ChannelsClient {
262
262
  * @throws {PluginUnexpectedError} For invalid/malformed responses
263
263
  */
264
264
  validateResponse(responseBody) {
265
- if (!responseBody || typeof responseBody !== "object") {
266
- throw new errors_1.PluginUnexpectedError("Empty or invalid response from plugin");
265
+ if (!responseBody || typeof responseBody !== 'object') {
266
+ throw new errors_1.PluginUnexpectedError('Empty or invalid response from plugin');
267
267
  }
268
268
  const response = responseBody;
269
269
  if (response.success === undefined) {
270
- throw new errors_1.PluginUnexpectedError("Malformed response: missing success field");
270
+ throw new errors_1.PluginUnexpectedError('Malformed response: missing success field');
271
271
  }
272
272
  return response;
273
273
  }
@@ -278,7 +278,8 @@ class ChannelsClient {
278
278
  * @param metadata Optional metadata (logs and traces)
279
279
  * @returns Data with metadata merged if present
280
280
  */
281
- mergeMetadata(data, metadata) {
281
+ mergeMetadata(data, metadata // eslint-disable-line @typescript-eslint/no-explicit-any
282
+ ) {
282
283
  if (!metadata || (!metadata.logs && !metadata.traces)) {
283
284
  return data;
284
285
  }
@@ -307,10 +308,8 @@ class ChannelsClient {
307
308
  const response = this.validateResponse(responseBody);
308
309
  // Handle execution errors
309
310
  if (!response.success) {
310
- const errorDetails = response.metadata
311
- ? { ...response.data, metadata: response.metadata }
312
- : response.data;
313
- throw new errors_1.PluginExecutionError(response.error || "Plugin execution failed", errorDetails);
311
+ const errorDetails = response.metadata ? { ...response.data, metadata: response.metadata } : response.data;
312
+ throw new errors_1.PluginExecutionError(response.error || 'Plugin execution failed', errorDetails);
314
313
  }
315
314
  // Return data with metadata if present
316
315
  return this.mergeMetadata(response.data, response.metadata);
@@ -327,7 +326,7 @@ class ChannelsClient {
327
326
  const response = await this.pluginsApi.callPlugin(this.pluginId, payload);
328
327
  return response.data;
329
328
  }
330
- const response = await this.axiosClient.post("/", payload);
329
+ const response = await this.axiosClient.post('/', payload);
331
330
  return response.data;
332
331
  }
333
332
  }
@@ -2,7 +2,7 @@
2
2
  * Base class for all plugin-related errors
3
3
  */
4
4
  export declare abstract class PluginClientError extends Error {
5
- abstract readonly category: "transport" | "execution" | "client";
5
+ abstract readonly category: 'transport' | 'execution' | 'client';
6
6
  }
7
7
  /**
8
8
  * HTTP/Network transport failures
@@ -16,12 +16,13 @@ exports.PluginClientError = PluginClientError;
16
16
  * - Invalid responses (malformed JSON)
17
17
  */
18
18
  class PluginTransportError extends PluginClientError {
19
- constructor(message, statusCode, errorDetails) {
19
+ constructor(message, statusCode, errorDetails // eslint-disable-line @typescript-eslint/no-explicit-any
20
+ ) {
20
21
  super(message);
21
22
  this.statusCode = statusCode;
22
23
  this.errorDetails = errorDetails;
23
- this.category = "transport";
24
- this.name = "PluginTransportError";
24
+ this.category = 'transport';
25
+ this.name = 'PluginTransportError';
25
26
  }
26
27
  }
27
28
  exports.PluginTransportError = PluginTransportError;
@@ -34,11 +35,12 @@ exports.PluginTransportError = PluginTransportError;
34
35
  * - On-chain failures (transaction reverted)
35
36
  */
36
37
  class PluginExecutionError extends PluginClientError {
37
- constructor(message, errorDetails) {
38
+ constructor(message, errorDetails // eslint-disable-line @typescript-eslint/no-explicit-any
39
+ ) {
38
40
  super(message);
39
41
  this.errorDetails = errorDetails;
40
- this.category = "execution";
41
- this.name = "PluginExecutionError";
42
+ this.category = 'execution';
43
+ this.name = 'PluginExecutionError';
42
44
  }
43
45
  }
44
46
  exports.PluginExecutionError = PluginExecutionError;
@@ -51,11 +53,12 @@ exports.PluginExecutionError = PluginExecutionError;
51
53
  * - Unexpected response structure
52
54
  */
53
55
  class PluginUnexpectedError extends PluginClientError {
54
- constructor(message, errorDetails) {
56
+ constructor(message, errorDetails // eslint-disable-line @typescript-eslint/no-explicit-any
57
+ ) {
55
58
  super(message);
56
59
  this.errorDetails = errorDetails;
57
- this.category = "client";
58
- this.name = "PluginUnexpectedError";
60
+ this.category = 'client';
61
+ this.name = 'PluginUnexpectedError';
59
62
  }
60
63
  }
61
64
  exports.PluginUnexpectedError = PluginUnexpectedError;
@@ -4,7 +4,7 @@
4
4
  * Unified client for interacting with the channels plugin
5
5
  * in both direct HTTP mode and OpenZeppelin Relayer mode.
6
6
  */
7
- export { ChannelsClient } from "./channels-client";
8
- export { ChannelsClientConfig, DirectHttpConfig, RelayerConfig, ChannelsXdrRequest, ChannelsFuncAuthRequest, ChannelsTransactionResponse, ListChannelAccountsResponse, SetChannelAccountsResponse, GetFeeUsageResponse, GetFeeLimitResponse, SetFeeLimitResponse, DeleteFeeLimitResponse, } from "./types";
9
- export { PluginClientError, PluginTransportError, PluginExecutionError, PluginUnexpectedError, } from "./errors";
7
+ export { ChannelsClient } from './channels-client';
8
+ export { ChannelsClientConfig, DirectHttpConfig, RelayerConfig, ChannelsXdrRequest, ChannelsFuncAuthRequest, ChannelsTransactionResponse, ListChannelAccountsResponse, SetChannelAccountsResponse, GetFeeUsageResponse, GetFeeLimitResponse, SetFeeLimitResponse, DeleteFeeLimitResponse, } from './types';
9
+ export { PluginClientError, PluginTransportError, PluginExecutionError, PluginUnexpectedError } from './errors';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { LogEntry } from "@openzeppelin/relayer-sdk";
1
+ import type { LogEntry } from '@openzeppelin/relayer-sdk';
2
2
  /**
3
3
  * Configuration for ChannelsClient in direct HTTP mode
4
4
  */
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@
4
4
  * Main entry point for the @openzeppelin/relayer-plugin-channels package
5
5
  * Re-exports both the client (for external use) and the plugin handler
6
6
  */
7
- export * from "./client";
8
- export * from "./plugin";
7
+ export * from './client';
8
+ export * from './plugin';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Transaction rebuild logic to use channel account as source.
5
5
  */
6
- import { Transaction } from "@stellar/stellar-sdk";
6
+ import { Transaction } from '@stellar/stellar-sdk';
7
7
  export interface RebuildParams {
8
8
  inputXdr: string;
9
9
  channelAddress: string;
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/plugin/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAIhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CA6GrE"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/plugin/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAe,MAAM,sBAAsB,CAAC;AAIhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CA2FrE"}
@@ -16,30 +16,28 @@ const constants_1 = require("./constants");
16
16
  * - Copy operations ensuring their source equals the fund address
17
17
  */
18
18
  function rebuildWithChannel(params) {
19
- const { inputXdr, channelAddress, channelSequence, fundAddress, networkPassphrase, } = params;
19
+ const { inputXdr, channelAddress, channelSequence, fundAddress, networkPassphrase } = params;
20
20
  // Parse input transaction
21
21
  let inputTx;
22
22
  try {
23
- const envelope = stellar_sdk_1.xdr.TransactionEnvelope.fromXDR(inputXdr, "base64");
23
+ const envelope = stellar_sdk_1.xdr.TransactionEnvelope.fromXDR(inputXdr, 'base64');
24
24
  // Ensure it's a regular transaction envelope (not fee bump)
25
25
  if (envelope.switch() !== stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
26
- throw (0, relayer_sdk_1.pluginError)("Input must be a regular transaction envelope (not fee bump)", {
27
- code: "INVALID_ENVELOPE_TYPE",
26
+ throw (0, relayer_sdk_1.pluginError)('Input must be a regular transaction envelope (not fee bump)', {
27
+ code: 'INVALID_ENVELOPE_TYPE',
28
28
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
29
29
  });
30
30
  }
31
31
  inputTx = new stellar_sdk_1.Transaction(envelope, networkPassphrase);
32
32
  }
33
33
  catch (error) {
34
- if (error.code === "INVALID_ENVELOPE_TYPE") {
34
+ if (error.code === 'INVALID_ENVELOPE_TYPE') {
35
35
  throw error;
36
36
  }
37
- throw (0, relayer_sdk_1.pluginError)("Failed to parse input transaction XDR", {
38
- code: "INVALID_XDR",
37
+ throw (0, relayer_sdk_1.pluginError)('Failed to parse input transaction XDR', {
38
+ code: 'INVALID_XDR',
39
39
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
40
- details: {
41
- message: error instanceof Error ? error.message : String(error),
42
- },
40
+ details: { message: error instanceof Error ? error.message : String(error) },
43
41
  });
44
42
  }
45
43
  // Validate timeBounds
@@ -50,7 +48,7 @@ function rebuildWithChannel(params) {
50
48
  const maxAllowedTime = now + constants_1.TIME.MAX_TIME_BOUND_OFFSET_SECONDS;
51
49
  if (maxTime > maxAllowedTime) {
52
50
  throw (0, relayer_sdk_1.pluginError)(`Transaction maxTime is too far in the future. Max allowed: ${constants_1.TIME.MAX_TIME_BOUND_OFFSET_SECONDS} seconds from now`, {
53
- code: "INVALID_TIME_BOUNDS",
51
+ code: 'INVALID_TIME_BOUNDS',
54
52
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
55
53
  details: { maxTime, maxAllowedTime },
56
54
  });
@@ -62,13 +60,9 @@ function rebuildWithChannel(params) {
62
60
  const op = inputTx.operations[i];
63
61
  if (op.source && op.source !== fundAddress) {
64
62
  throw (0, relayer_sdk_1.pluginError)(`Operation ${i} has source ${op.source} but must be ${fundAddress} or omitted`, {
65
- code: "INVALID_OPERATION_SOURCE",
63
+ code: 'INVALID_OPERATION_SOURCE',
66
64
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
67
- details: {
68
- operationIndex: i,
69
- operationSource: op.source,
70
- expectedSource: fundAddress,
71
- },
65
+ details: { operationIndex: i, operationSource: op.source, expectedSource: fundAddress },
72
66
  });
73
67
  }
74
68
  }
@@ -5,12 +5,14 @@
5
5
  */
6
6
  export interface ChannelAccountsConfig {
7
7
  fundRelayerId: string;
8
- network: "testnet" | "mainnet";
8
+ network: 'testnet' | 'mainnet';
9
9
  lockTtlSeconds: number;
10
10
  adminSecret?: string;
11
11
  feeLimit?: number;
12
12
  feeResetPeriodMs?: number;
13
13
  apiKeyHeader: string;
14
+ limitedContracts: Set<string>;
15
+ contractCapacityRatio: number;
14
16
  }
15
17
  /**
16
18
  * Load configuration from environment variables
@@ -19,5 +21,5 @@ export declare function loadConfig(): ChannelAccountsConfig;
19
21
  /**
20
22
  * Get the network passphrase based on the configuration
21
23
  */
22
- export declare function getNetworkPassphrase(network: "testnet" | "mainnet"): string;
24
+ export declare function getNetworkPassphrase(network: 'testnet' | 'mainnet'): string;
23
25
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/plugin/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAwDD;;GAEG;AACH,wBAAgB,UAAU,IAAI,qBAAqB,CAkBlD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAE3E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/plugin/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAoFD;;GAEG;AACH,wBAAgB,UAAU,IAAI,qBAAqB,CAoBlD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAE3E"}