@openzeppelin/relayer-plugin-channels 0.6.0 → 0.8.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 (51) 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 +5 -1
  10. package/dist/client/types.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/plugin/build.d.ts +1 -1
  13. package/dist/plugin/build.d.ts.map +1 -1
  14. package/dist/plugin/build.js +11 -17
  15. package/dist/plugin/config.d.ts +4 -2
  16. package/dist/plugin/config.d.ts.map +1 -1
  17. package/dist/plugin/config.js +43 -13
  18. package/dist/plugin/constants.d.ts +1 -0
  19. package/dist/plugin/constants.d.ts.map +1 -1
  20. package/dist/plugin/constants.js +2 -1
  21. package/dist/plugin/fee-tracking.d.ts +2 -2
  22. package/dist/plugin/fee-tracking.d.ts.map +1 -1
  23. package/dist/plugin/fee-tracking.js +4 -9
  24. package/dist/plugin/fee.d.ts +12 -5
  25. package/dist/plugin/fee.d.ts.map +1 -1
  26. package/dist/plugin/fee.js +39 -23
  27. package/dist/plugin/handler.d.ts +2 -2
  28. package/dist/plugin/handler.d.ts.map +1 -1
  29. package/dist/plugin/handler.js +52 -34
  30. package/dist/plugin/index.d.ts +1 -1
  31. package/dist/plugin/management.d.ts +1 -1
  32. package/dist/plugin/management.d.ts.map +1 -1
  33. package/dist/plugin/management.js +53 -79
  34. package/dist/plugin/pool.d.ts +8 -3
  35. package/dist/plugin/pool.d.ts.map +1 -1
  36. package/dist/plugin/pool.js +45 -17
  37. package/dist/plugin/simulation.d.ts +34 -6
  38. package/dist/plugin/simulation.d.ts.map +1 -1
  39. package/dist/plugin/simulation.js +86 -49
  40. package/dist/plugin/submit.d.ts +5 -5
  41. package/dist/plugin/submit.d.ts.map +1 -1
  42. package/dist/plugin/submit.js +11 -14
  43. package/dist/plugin/tx.d.ts +1 -1
  44. package/dist/plugin/tx.d.ts.map +1 -1
  45. package/dist/plugin/tx.js +6 -8
  46. package/dist/plugin/types.d.ts +8 -4
  47. package/dist/plugin/types.d.ts.map +1 -1
  48. package/dist/plugin/validation.d.ts +1 -1
  49. package/dist/plugin/validation.d.ts.map +1 -1
  50. package/dist/plugin/validation.js +26 -27
  51. package/package.json +1 -1
@@ -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"}
@@ -12,9 +12,9 @@ const relayer_sdk_1 = require("@openzeppelin/relayer-sdk");
12
12
  const constants_1 = require("./constants");
13
13
  function requireEnv(name) {
14
14
  const v = process.env[name];
15
- if (!v || v.trim() === "") {
15
+ if (!v || v.trim() === '') {
16
16
  throw (0, relayer_sdk_1.pluginError)(`Missing required environment variable: ${name}`, {
17
- code: "CONFIG_MISSING",
17
+ code: 'CONFIG_MISSING',
18
18
  status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
19
19
  details: { name },
20
20
  });
@@ -33,9 +33,7 @@ function parseLockTtl() {
33
33
  if (!raw)
34
34
  return constants_1.CONFIG.DEFAULT_LOCK_TTL_SECONDS;
35
35
  const n = Number(raw);
36
- if (!Number.isFinite(n) ||
37
- n < constants_1.CONFIG.MIN_LOCK_TTL_SECONDS ||
38
- n > constants_1.CONFIG.MAX_LOCK_TTL_SECONDS) {
36
+ if (!Number.isFinite(n) || n < constants_1.CONFIG.MIN_LOCK_TTL_SECONDS || n > constants_1.CONFIG.MAX_LOCK_TTL_SECONDS) {
39
37
  return constants_1.CONFIG.DEFAULT_LOCK_TTL_SECONDS;
40
38
  }
41
39
  return Math.floor(n);
@@ -57,34 +55,66 @@ function parseFeeResetPeriod() {
57
55
  function parseApiKeyHeader() {
58
56
  const raw = process.env.API_KEY_HEADER;
59
57
  if (!raw)
60
- return "x-api-key";
58
+ return 'x-api-key';
61
59
  const trimmed = raw.trim().toLowerCase();
62
- return trimmed.length > 0 ? trimmed : "x-api-key";
60
+ return trimmed.length > 0 ? trimmed : 'x-api-key';
61
+ }
62
+ function parseLimitedContracts() {
63
+ const raw = process.env.LIMITED_CONTRACTS;
64
+ if (!raw)
65
+ return new Set();
66
+ const contracts = raw
67
+ .split(',')
68
+ .map((s) => s.trim().toUpperCase())
69
+ .filter((s) => s.length > 0);
70
+ // Validate each contract address
71
+ for (const contract of contracts) {
72
+ if (!stellar_sdk_1.StrKey.isValidContract(contract)) {
73
+ throw (0, relayer_sdk_1.pluginError)(`Invalid contract address in LIMITED_CONTRACTS: ${contract}`, {
74
+ code: 'CONFIG_INVALID',
75
+ status: constants_1.HTTP_STATUS.INTERNAL_SERVER_ERROR,
76
+ details: { contract },
77
+ });
78
+ }
79
+ }
80
+ return new Set(contracts);
81
+ }
82
+ function parseContractCapacityRatio() {
83
+ const raw = process.env.CONTRACT_CAPACITY_RATIO;
84
+ if (!raw)
85
+ return constants_1.CONFIG.DEFAULT_CONTRACT_CAPACITY_RATIO;
86
+ const n = Number(raw);
87
+ if (!Number.isFinite(n) || n < 0 || n > 1) {
88
+ return constants_1.CONFIG.DEFAULT_CONTRACT_CAPACITY_RATIO;
89
+ }
90
+ return n;
63
91
  }
64
92
  /**
65
93
  * Load configuration from environment variables
66
94
  */
67
95
  function loadConfig() {
68
- const networkRaw = requireEnv("STELLAR_NETWORK").toLowerCase();
69
- if (networkRaw !== "testnet" && networkRaw !== "mainnet") {
96
+ const networkRaw = requireEnv('STELLAR_NETWORK').toLowerCase();
97
+ if (networkRaw !== 'testnet' && networkRaw !== 'mainnet') {
70
98
  throw (0, relayer_sdk_1.pluginError)('STELLAR_NETWORK must be "testnet" or "mainnet"', {
71
- code: "UNSUPPORTED_NETWORK",
99
+ code: 'UNSUPPORTED_NETWORK',
72
100
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
73
101
  });
74
102
  }
75
103
  return {
76
- fundRelayerId: requireEnv("FUND_RELAYER_ID"),
104
+ fundRelayerId: requireEnv('FUND_RELAYER_ID'),
77
105
  network: networkRaw,
78
106
  lockTtlSeconds: parseLockTtl(),
79
- adminSecret: parseOptionalString("PLUGIN_ADMIN_SECRET"),
107
+ adminSecret: parseOptionalString('PLUGIN_ADMIN_SECRET'),
80
108
  feeLimit: parseFeeLimit(),
81
109
  feeResetPeriodMs: parseFeeResetPeriod(),
82
110
  apiKeyHeader: parseApiKeyHeader(),
111
+ limitedContracts: parseLimitedContracts(),
112
+ contractCapacityRatio: parseContractCapacityRatio(),
83
113
  };
84
114
  }
85
115
  /**
86
116
  * Get the network passphrase based on the configuration
87
117
  */
88
118
  function getNetworkPassphrase(network) {
89
- return network === "mainnet" ? stellar_sdk_1.Networks.PUBLIC : stellar_sdk_1.Networks.TESTNET;
119
+ return network === 'mainnet' ? stellar_sdk_1.Networks.PUBLIC : stellar_sdk_1.Networks.TESTNET;
90
120
  }
@@ -18,6 +18,7 @@ export declare const CONFIG: {
18
18
  readonly DEFAULT_LOCK_TTL_SECONDS: 30;
19
19
  readonly MIN_LOCK_TTL_SECONDS: 3;
20
20
  readonly MAX_LOCK_TTL_SECONDS: 30;
21
+ readonly DEFAULT_CONTRACT_CAPACITY_RATIO: 0.8;
21
22
  };
22
23
  export declare const POOL: {
23
24
  readonly MUTEX_TTL_SECONDS: 1;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/plugin/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAGX,eAAO,MAAM,MAAM;;;;CAIT,CAAC;AAGX,eAAO,MAAM,IAAI;;;;;CAOP,CAAC;AAGX,eAAO,MAAM,IAAI;;CAEP,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAGX,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAEX,eAAO,MAAM,GAAG;;CAGN,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/plugin/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAGX,eAAO,MAAM,MAAM;;;;;CAKT,CAAC;AAGX,eAAO,MAAM,IAAI;;;;;CAOP,CAAC;AAGX,eAAO,MAAM,IAAI;;CAEP,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAGX,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAEX,eAAO,MAAM,GAAG;;CAGN,CAAC"}
@@ -23,6 +23,7 @@ exports.CONFIG = {
23
23
  DEFAULT_LOCK_TTL_SECONDS: 30,
24
24
  MIN_LOCK_TTL_SECONDS: 3,
25
25
  MAX_LOCK_TTL_SECONDS: 30,
26
+ DEFAULT_CONTRACT_CAPACITY_RATIO: 0.8,
26
27
  };
27
28
  // Pool Constants
28
29
  exports.POOL = {
@@ -39,7 +40,7 @@ exports.TIME = {
39
40
  };
40
41
  // Simulation-related defaults
41
42
  exports.SIMULATION = {
42
- DEFAULT_FEE: "100",
43
+ DEFAULT_FEE: '100',
43
44
  MIN_TIME_BOUND: 0,
44
45
  MAX_TIME_BOUND_OFFSET_SECONDS: 30,
45
46
  MAX_FUTURE_TIME_BOUND_SECONDS: 30,
@@ -5,7 +5,7 @@
5
5
  * Supports custom per-key limits with fallback to default.
6
6
  * Supports periodic reset of consumption.
7
7
  */
8
- import { PluginKVStore } from "@openzeppelin/relayer-sdk";
8
+ import { PluginKVStore } from '@openzeppelin/relayer-sdk';
9
9
  /** KV data structure for fee consumption */
10
10
  export interface FeeData {
11
11
  consumed: number;
@@ -22,7 +22,7 @@ export interface UsageInfo {
22
22
  /** Configuration for FeeTracker */
23
23
  export interface FeeTrackerConfig {
24
24
  kv: PluginKVStore;
25
- network: "testnet" | "mainnet";
25
+ network: 'testnet' | 'mainnet';
26
26
  apiKey: string;
27
27
  defaultLimit?: number;
28
28
  resetPeriodMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"fee-tracking.d.ts","sourceRoot":"","sources":["../../src/plugin/fee-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAGvE,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,aAAa,CAAC;IAClB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;gBAE5B,MAAM,EAAE,gBAAgB;IAUpC;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7C;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAqBxC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAKnD;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxC;;;OAGG;YACW,WAAW;IASzB;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,OAAO,GAElB;CACF"}
1
+ {"version":3,"file":"fee-tracking.d.ts","sourceRoot":"","sources":["../../src/plugin/fee-tracking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AAGvE,4CAA4C;AAC5C,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,aAAa,CAAC;IAClB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;gBAE5B,MAAM,EAAE,gBAAgB;IAUpC;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7C;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B7C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAexC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAKnD;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxC;;;OAGG;YACW,WAAW;IASzB;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,OAAO,GAElB;CACF"}
@@ -31,7 +31,7 @@ class FeeTracker {
31
31
  if (consumed + fee > limit) {
32
32
  const remaining = limit - consumed;
33
33
  throw (0, relayer_sdk_1.pluginError)(`Transaction fee (${fee} stroops) exceeds remaining budget (${remaining} stroops). Consumed: ${consumed}/${limit} stroops.`, {
34
- code: "FEE_LIMIT_EXCEEDED",
34
+ code: 'FEE_LIMIT_EXCEEDED',
35
35
  status: constants_1.HTTP_STATUS.TOO_MANY_REQUESTS,
36
36
  details: { consumed, fee, remaining, limit },
37
37
  });
@@ -54,7 +54,7 @@ class FeeTracker {
54
54
  periodStart: state.periodStart ?? now,
55
55
  });
56
56
  return true;
57
- }, { ttlSec: 5, onBusy: "skip" });
57
+ }, { ttlSec: 5, onBusy: 'skip' });
58
58
  if (result !== null)
59
59
  return;
60
60
  // Lock busy, retry with jitter
@@ -71,17 +71,12 @@ class FeeTracker {
71
71
  * Get complete usage info for management API.
72
72
  */
73
73
  async getUsageInfo() {
74
- const [state, limit] = await Promise.all([
75
- this.getFeeState(),
76
- this.getEffectiveLimit(),
77
- ]);
74
+ const [state, limit] = await Promise.all([this.getFeeState(), this.getEffectiveLimit()]);
78
75
  return {
79
76
  consumed: state.consumed,
80
77
  limit,
81
78
  remaining: limit !== undefined ? Math.max(0, limit - state.consumed) : undefined,
82
- periodStartAt: state.periodStart
83
- ? new Date(state.periodStart).toISOString()
84
- : undefined,
79
+ periodStartAt: state.periodStart ? new Date(state.periodStart).toISOString() : undefined,
85
80
  periodEndsAt: state.periodStart && this.resetPeriodMs
86
81
  ? new Date(state.periodStart + this.resetPeriodMs).toISOString()
87
82
  : undefined,
@@ -4,11 +4,18 @@
4
4
  * Static fee calculation for fee bump submissions matching launchtube.
5
5
  * - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
6
6
  * - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
7
- * - KALE contract gets reduced fee (BASE_FEE * 2 + 1)
7
+ * - Limited contracts (from LIMITED_CONTRACTS env) get reduced fee (BASE_FEE * 2 + 1)
8
8
  */
9
- import { Transaction } from "@stellar/stellar-sdk";
10
- export declare const KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
9
+ import { Transaction, xdr } from '@stellar/stellar-sdk';
11
10
  export declare const INCLUSION_FEE_DEFAULT: number;
12
- export declare const INCLUSION_FEE_KALE: number;
13
- export declare function calculateMaxFee(transaction: Transaction): number;
11
+ export declare const INCLUSION_FEE_LIMITED: number;
12
+ /**
13
+ * Extract contract ID from a HostFunction (for func+auth flow)
14
+ */
15
+ export declare function getContractIdFromFunc(func: xdr.HostFunction): string | undefined;
16
+ /**
17
+ * Extract contract ID from a Transaction (for XDR flow)
18
+ */
19
+ export declare function getContractIdFromTransaction(transaction: Transaction): string | undefined;
20
+ export declare function calculateMaxFee(transaction: Transaction, limitedContracts?: Set<string>): number;
14
21
  //# sourceMappingURL=fee.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAEL,WAAW,EAIZ,MAAM,sBAAsB,CAAC;AAG9B,eAAO,MAAM,aAAa,6DACkC,CAAC;AAC7D,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA2B,CAAC;AA8B3D,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAsBhE"}
1
+ {"version":3,"file":"fee.d.ts","sourceRoot":"","sources":["../../src/plugin/fee.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,WAAW,EAAE,GAAG,EAAqB,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAC9D,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAE9D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,YAAY,GAAG,MAAM,GAAG,SAAS,CAUhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAYzF;AASD,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,MAAM,CAoB3G"}
@@ -5,39 +5,56 @@
5
5
  * Static fee calculation for fee bump submissions matching launchtube.
6
6
  * - For Soroban transactions: use resourceFee + inclusion fee (BASE_FEE * 2 + 3)
7
7
  * - For non-Soroban: use NON_SOROBAN_FEE + inclusion fee
8
- * - KALE contract gets reduced fee (BASE_FEE * 2 + 1)
8
+ * - Limited contracts (from LIMITED_CONTRACTS env) get reduced fee (BASE_FEE * 2 + 1)
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.INCLUSION_FEE_KALE = exports.INCLUSION_FEE_DEFAULT = exports.KALE_CONTRACT = void 0;
11
+ exports.INCLUSION_FEE_LIMITED = exports.INCLUSION_FEE_DEFAULT = void 0;
12
+ exports.getContractIdFromFunc = getContractIdFromFunc;
13
+ exports.getContractIdFromTransaction = getContractIdFromTransaction;
12
14
  exports.calculateMaxFee = calculateMaxFee;
13
15
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
14
16
  const constants_1 = require("./constants");
15
- exports.KALE_CONTRACT = "CDL74RF5BLYR2YBLCCI7F5FB6TPSCLKEJUBSD2RSVWZ4YHF3VMFAIGWA";
16
17
  exports.INCLUSION_FEE_DEFAULT = Number(stellar_sdk_1.BASE_FEE) * 2 + 3;
17
- exports.INCLUSION_FEE_KALE = Number(stellar_sdk_1.BASE_FEE) * 2 + 1;
18
- function getInclusionFee(transaction) {
18
+ exports.INCLUSION_FEE_LIMITED = Number(stellar_sdk_1.BASE_FEE) * 2 + 1;
19
+ /**
20
+ * Extract contract ID from a HostFunction (for func+auth flow)
21
+ */
22
+ function getContractIdFromFunc(func) {
23
+ try {
24
+ if (func.switch() !== stellar_sdk_1.xdr.HostFunctionType.hostFunctionTypeInvokeContract()) {
25
+ return undefined;
26
+ }
27
+ const invokeContract = func.invokeContract();
28
+ return stellar_sdk_1.StrKey.encodeContract(invokeContract.contractAddress().contractId());
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ /**
35
+ * Extract contract ID from a Transaction (for XDR flow)
36
+ */
37
+ function getContractIdFromTransaction(transaction) {
19
38
  try {
20
39
  if (transaction.operations.length !== 1)
21
- return exports.INCLUSION_FEE_DEFAULT;
40
+ return undefined;
22
41
  const op = transaction.operations[0];
23
- if (op.type !== "invokeHostFunction")
24
- return exports.INCLUSION_FEE_DEFAULT;
42
+ if (op.type !== 'invokeHostFunction')
43
+ return undefined;
25
44
  const invokeOp = op;
26
- if (invokeOp.func.switch() !==
27
- stellar_sdk_1.xdr.HostFunctionType.hostFunctionTypeInvokeContract()) {
28
- return exports.INCLUSION_FEE_DEFAULT;
29
- }
30
- const invokeContract = invokeOp.func.invokeContract();
31
- const contract = stellar_sdk_1.StrKey.encodeContract(invokeContract.contractAddress().contractId());
32
- return contract === exports.KALE_CONTRACT
33
- ? exports.INCLUSION_FEE_KALE
34
- : exports.INCLUSION_FEE_DEFAULT;
45
+ return getContractIdFromFunc(invokeOp.func);
35
46
  }
36
47
  catch {
37
- return exports.INCLUSION_FEE_DEFAULT;
48
+ return undefined;
49
+ }
50
+ }
51
+ function getInclusionFee(contractId, limitedContracts) {
52
+ if (contractId && limitedContracts.has(contractId)) {
53
+ return exports.INCLUSION_FEE_LIMITED;
38
54
  }
55
+ return exports.INCLUSION_FEE_DEFAULT;
39
56
  }
40
- function calculateMaxFee(transaction) {
57
+ function calculateMaxFee(transaction, limitedContracts = new Set()) {
41
58
  const envelope = transaction.toEnvelope();
42
59
  let resourceFee = 0n;
43
60
  if (envelope.switch() === stellar_sdk_1.xdr.EnvelopeType.envelopeTypeTx()) {
@@ -46,10 +63,9 @@ function calculateMaxFee(transaction) {
46
63
  resourceFee = sorobanData.resourceFee().toBigInt();
47
64
  }
48
65
  }
49
- const inclusionFee = getInclusionFee(transaction);
50
- const fee = resourceFee > 0n
51
- ? resourceFee + BigInt(inclusionFee)
52
- : BigInt(constants_1.FEE.NON_SOROBAN_FEE + inclusionFee);
66
+ const contractId = getContractIdFromTransaction(transaction);
67
+ const inclusionFee = getInclusionFee(contractId, limitedContracts);
68
+ const fee = resourceFee > 0n ? resourceFee + BigInt(inclusionFee) : BigInt(constants_1.FEE.NON_SOROBAN_FEE + inclusionFee);
53
69
  console.debug(`[channels] Calculated max_fee: ${Number(fee)} stroops (resourceFee: ${resourceFee}, inclusionFee: ${inclusionFee})`);
54
70
  return Number(fee);
55
71
  }
@@ -4,8 +4,8 @@
4
4
  * Main handler for the channel accounts plugin.
5
5
  * Orchestrates the transaction processing pipeline using channel accounts with fee bumping.
6
6
  */
7
- import { PluginContext } from "@openzeppelin/relayer-sdk";
8
- import { Transaction, xdr } from "@stellar/stellar-sdk";
7
+ import { PluginContext } from '@openzeppelin/relayer-sdk';
8
+ import { Transaction, xdr } from '@stellar/stellar-sdk';
9
9
  /**
10
10
  * Extracts func and auth from an unsigned Soroban transaction.
11
11
  * Returns null if the transaction is not a single invokeHostFunction operation.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/plugin/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AASvE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAcxD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,WAAW,GACd;IAAE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,CAAA;CAAE,GAAG,IAAI,CAkB1E;AAiOD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/plugin/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAe,MAAM,2BAA2B,CAAC;AASvE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAWxD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,WAAW,GACd;IAAE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC,yBAAyB,EAAE,CAAA;CAAE,GAAG,IAAI,CAkB1E;AAsOD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAElE"}
@@ -44,14 +44,14 @@ function extractFuncAuthFromUnsignedXdr(tx) {
44
44
  auth: invokeHostFn.auth(),
45
45
  };
46
46
  }
47
- async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networkPassphrase, api, pool, tracker) {
47
+ async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networkPassphrase, api, pool, acquireOptions, tracker) {
48
48
  const tx = new stellar_sdk_1.Transaction(xdrStr, networkPassphrase);
49
49
  // Unsigned XDR: extract func+auth and route through channel path
50
50
  if (tx.signatures.length === 0) {
51
51
  const extracted = extractFuncAuthFromUnsignedXdr(tx);
52
52
  if (!extracted) {
53
- throw (0, relayer_sdk_1.pluginError)("Unsigned XDR must contain exactly one invokeHostFunction operation", {
54
- code: "INVALID_UNSIGNED_XDR",
53
+ throw (0, relayer_sdk_1.pluginError)('Unsigned XDR must contain exactly one invokeHostFunction operation', {
54
+ code: 'INVALID_UNSIGNED_XDR',
55
55
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
56
56
  details: {
57
57
  operationCount: tx.operations.length,
@@ -60,41 +60,54 @@ async function handleXdrSubmit(xdrStr, fundRelayer, fundAddress, network, networ
60
60
  });
61
61
  }
62
62
  console.log(`[channels] Unsigned XDR detected, extracting func+auth and routing through channel path`);
63
- return handleFuncAuthSubmit(extracted.func, extracted.auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, tracker);
63
+ // Update acquireOptions with contractId from extracted func
64
+ const contractId = (0, fee_1.getContractIdFromFunc)(extracted.func);
65
+ const updatedOptions = { ...acquireOptions, contractId };
66
+ return handleFuncAuthSubmit(extracted.func, extracted.auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, updatedOptions, tracker);
64
67
  }
65
68
  const validated = (0, tx_1.validateExistingTransactionForSubmitOnly)(tx);
66
- const maxFee = (0, fee_1.calculateMaxFee)(validated);
69
+ const maxFee = (0, fee_1.calculateMaxFee)(validated, acquireOptions.limitedContracts);
67
70
  await tracker?.checkBudget(maxFee);
68
71
  return (0, submit_1.submitWithFeeBumpAndWait)(fundRelayer, validated.toXDR(), network, maxFee, api, tracker);
69
72
  }
70
- async function handleFuncAuthSubmit(func, auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, tracker) {
73
+ async function handleFuncAuthSubmit(func, auth, api, pool, fundRelayer, fundAddress, network, networkPassphrase, acquireOptions, tracker) {
74
+ // Simulate once — used for both read-only detection and transaction assembly
75
+ const simulation = await (0, simulation_1.simulateTransaction)(func, auth, fundAddress, fundRelayer, networkPassphrase);
76
+ if (simulation.isReadOnly) {
77
+ console.log(`[channels] Read-only call detected, returning simulation result`);
78
+ return {
79
+ transactionId: null,
80
+ status: 'readonly',
81
+ hash: null,
82
+ returnValue: simulation.returnValue,
83
+ latestLedger: simulation.latestLedger,
84
+ };
85
+ }
71
86
  let poolLock;
72
87
  try {
73
- poolLock = await pool.acquire();
88
+ poolLock = await pool.acquire(acquireOptions);
74
89
  const channelRelayer = api.useRelayer(poolLock.relayerId);
75
90
  const channelInfo = await channelRelayer.getRelayer();
76
91
  console.log(`[channels] Acquired channel: ${poolLock.relayerId}`);
77
92
  if (!channelInfo || !channelInfo.address) {
78
- throw (0, relayer_sdk_1.pluginError)("Channel relayer not found", {
79
- code: "RELAYER_UNAVAILABLE",
93
+ throw (0, relayer_sdk_1.pluginError)('Channel relayer not found', {
94
+ code: 'RELAYER_UNAVAILABLE',
80
95
  status: constants_1.HTTP_STATUS.BAD_GATEWAY,
81
96
  details: { relayerId: poolLock.relayerId },
82
97
  });
83
98
  }
84
99
  const channelStatus = await channelRelayer.getRelayerStatus();
85
- if (channelStatus.network_type !== "stellar") {
86
- throw (0, relayer_sdk_1.pluginError)("Channel relayer network type must be stellar", {
87
- code: "UNSUPPORTED_NETWORK",
100
+ if (channelStatus.network_type !== 'stellar') {
101
+ throw (0, relayer_sdk_1.pluginError)('Channel relayer network type must be stellar', {
102
+ code: 'UNSUPPORTED_NETWORK',
88
103
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
89
- details: {
90
- network_type: channelStatus.network_type,
91
- relayerId: poolLock.relayerId,
92
- },
104
+ details: { network_type: channelStatus.network_type, relayerId: poolLock.relayerId },
93
105
  });
94
106
  }
95
- const built = await (0, simulation_1.simulateAndBuildWithChannel)(func, auth, { address: channelInfo.address, sequence: channelStatus.sequence_number }, fundAddress, fundRelayer, networkPassphrase);
107
+ // Assemble the transaction using the cached simulation result no second RPC call
108
+ const built = (0, simulation_1.buildWithChannel)(func, auth, { address: channelInfo.address, sequence: channelStatus.sequence_number }, networkPassphrase, simulation.rawSimResult);
96
109
  const signedTx = await (0, submit_1.signWithChannelAndFund)(built, channelRelayer, fundRelayer, channelInfo.address, fundAddress, networkPassphrase);
97
- const maxFee = (0, fee_1.calculateMaxFee)(signedTx);
110
+ const maxFee = (0, fee_1.calculateMaxFee)(signedTx, acquireOptions.limitedContracts);
98
111
  await tracker?.checkBudget(maxFee);
99
112
  return await (0, submit_1.submitWithFeeBumpAndWait)(fundRelayer, signedTx.toXDR(), network, maxFee, api, tracker);
100
113
  }
@@ -119,8 +132,8 @@ async function channelAccounts(context) {
119
132
  const apiKey = getApiKey(headers, config.apiKeyHeader);
120
133
  // If default limit is set, require API key
121
134
  if (config.feeLimit !== undefined && !apiKey) {
122
- throw (0, relayer_sdk_1.pluginError)("API key required", {
123
- code: "API_KEY_REQUIRED",
135
+ throw (0, relayer_sdk_1.pluginError)('API key required', {
136
+ code: 'API_KEY_REQUIRED',
124
137
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
125
138
  });
126
139
  }
@@ -136,34 +149,39 @@ async function channelAccounts(context) {
136
149
  }
137
150
  // 1. Validate and parse request (xdr OR func+auth)
138
151
  const request = (0, validation_1.validateAndParseRequest)(params);
139
- console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type === "func-auth" ? request.auth.length : "N/A"}`);
152
+ console.debug(`[channels] Request type: ${request.type}, auth entries: ${request.type === 'func-auth' ? request.auth.length : 'N/A'}`);
140
153
  // 2. Get fund relayer
141
154
  const fundRelayer = api.useRelayer(config.fundRelayerId);
142
155
  const fundInfo = await fundRelayer.getRelayer();
143
156
  if (!fundInfo || !fundInfo.address) {
144
- throw (0, relayer_sdk_1.pluginError)("Fund relayer not found", {
145
- code: "RELAYER_UNAVAILABLE",
157
+ throw (0, relayer_sdk_1.pluginError)('Fund relayer not found', {
158
+ code: 'RELAYER_UNAVAILABLE',
146
159
  status: constants_1.HTTP_STATUS.BAD_GATEWAY,
147
160
  details: { relayerId: config.fundRelayerId },
148
161
  });
149
162
  }
150
- if (fundInfo.network_type !== "stellar") {
151
- throw (0, relayer_sdk_1.pluginError)("Fund relayer network type must be stellar", {
152
- code: "UNSUPPORTED_NETWORK",
163
+ if (fundInfo.network_type !== 'stellar') {
164
+ throw (0, relayer_sdk_1.pluginError)('Fund relayer network type must be stellar', {
165
+ code: 'UNSUPPORTED_NETWORK',
153
166
  status: constants_1.HTTP_STATUS.BAD_REQUEST,
154
- details: {
155
- network_type: fundInfo.network_type,
156
- relayerId: config.fundRelayerId,
157
- },
167
+ details: { network_type: fundInfo.network_type, relayerId: config.fundRelayerId },
158
168
  });
159
169
  }
160
- // 3. Branch by request type
161
- if (request.type === "xdr") {
170
+ // 3. Build acquire options for contract capacity limits
171
+ const acquireOptions = {
172
+ limitedContracts: config.limitedContracts,
173
+ capacityRatio: config.contractCapacityRatio,
174
+ };
175
+ // 4. Branch by request type
176
+ if (request.type === 'xdr') {
162
177
  console.log(`[channels] Flow: XDR submit-only`);
163
- return await handleXdrSubmit(request.xdr, fundRelayer, fundInfo.address, config.network, networkPassphrase, api, pool, tracker);
178
+ return await handleXdrSubmit(request.xdr, fundRelayer, fundInfo.address, config.network, networkPassphrase, api, pool, acquireOptions, tracker);
164
179
  }
180
+ // Extract contractId for func+auth flow
181
+ const contractId = (0, fee_1.getContractIdFromFunc)(request.func);
182
+ const funcAcquireOptions = { ...acquireOptions, contractId };
165
183
  console.log(`[channels] Flow: func+auth with channel account`);
166
- return await handleFuncAuthSubmit(request.func, request.auth, api, pool, fundRelayer, fundInfo.address, config.network, networkPassphrase, tracker);
184
+ return await handleFuncAuthSubmit(request.func, request.auth, api, pool, fundRelayer, fundInfo.address, config.network, networkPassphrase, funcAcquireOptions, tracker);
167
185
  }
168
186
  /**
169
187
  * Main plugin handler exported for OpenZeppelin Relayer
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Exports the main handler function for the Channels plugin
5
5
  */
6
- export { handler } from "./handler";
6
+ export { handler } from './handler';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -9,7 +9,7 @@
9
9
  * - setFeeLimit: sets custom limit for an API key
10
10
  * - deleteFeeLimit: removes custom limit for an API key
11
11
  */
12
- import type { PluginContext } from "@openzeppelin/relayer-sdk";
12
+ import type { PluginContext } from '@openzeppelin/relayer-sdk';
13
13
  export declare function isManagementRequest(params: any): boolean;
14
14
  export declare function handleManagement(context: PluginContext): Promise<any>;
15
15
  //# sourceMappingURL=management.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/plugin/management.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,2BAA2B,CAAC;AAM9E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAOxD;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CA8C3E"}
1
+ {"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/plugin/management.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,2BAA2B,CAAC;AAM9E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAIxD;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAkC3E"}