@elisym/sdk 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.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Address, TransactionSigner, Rpc, SolanaRpcApi } from '@solana/kit';
2
2
  import { Filter, Event } from 'nostr-tools';
3
+ import { z } from 'zod';
3
4
 
4
5
  declare class ElisymIdentity {
5
6
  private _secretKey;
@@ -139,6 +140,18 @@ interface ElisymClientConfig {
139
140
  relays?: string[];
140
141
  }
141
142
 
143
+ /**
144
+ * Pluggable signer used by `PaymentStrategy.buildTransaction`.
145
+ *
146
+ * Aliased to Solana Kit's `TransactionSigner` so callers can pass a hot
147
+ * `KeyPairSigner`, an external KMS-backed signer, a hardware-wallet adapter,
148
+ * or any other implementation that conforms to the Solana Kit signer contract
149
+ * (TransactionPartialSigner / TransactionSendingSigner / TransactionModifyingSigner).
150
+ *
151
+ * Exposing the alias here lets downstream packages depend on `@elisym/sdk`'s
152
+ * abstraction instead of importing Kit directly when wiring custom signers.
153
+ */
154
+ type Signer = TransactionSigner;
142
155
  /**
143
156
  * Protocol fee + treasury inputs for building a payment request.
144
157
  *
@@ -173,18 +186,54 @@ interface PaymentStrategy {
173
186
  * Validate that a payment request has the correct recipient and protocol fee.
174
187
  * Returns a typed validation error if invalid, null if OK.
175
188
  */
176
- validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string): PaymentValidationError | null;
189
+ validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string, options?: {
190
+ maxAmountLamports?: bigint;
191
+ }): PaymentValidationError | null;
177
192
  /**
178
- * Build and sign a transaction from a payment request using a TransactionSigner.
179
- * Returns a chain-specific signed transaction value. The caller is responsible
180
- * for sending it (e.g. via `rpc.sendTransaction(...).send()`).
193
+ * Build and sign a transaction from a payment request using a `Signer`.
194
+ *
195
+ * The `Signer` parameter is intentionally the abstract interface, not a
196
+ * concrete `KeyPairSigner`, so callers can plug in external signers
197
+ * (KMS, hardware wallet, ElizaOS approval Action) without holding the
198
+ * raw secret key in process memory.
199
+ *
200
+ * Returns a chain-specific signed transaction value. The caller is
201
+ * responsible for sending it (e.g. via `rpc.sendTransaction(...).send()`).
181
202
  */
182
- buildTransaction(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput): Promise<unknown>;
203
+ buildTransaction(paymentRequest: PaymentRequestData, payerSigner: Signer, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput, options?: BuildTransactionOptions): Promise<unknown>;
183
204
  /**
184
205
  * Verify a payment on-chain.
185
206
  */
186
207
  verifyPayment(rpc: Rpc<SolanaRpcApi>, paymentRequest: PaymentRequestData, config: ProtocolConfigInput, options?: VerifyOptions): Promise<VerifyResult>;
187
208
  }
209
+ /**
210
+ * Optional knobs for `PaymentStrategy.buildTransaction`.
211
+ *
212
+ * Defaults are chosen for typical Solana mainnet conditions; override these
213
+ * when the caller knows peak fees are elevated, when running against a
214
+ * private cluster with no priority-fee samples, or when bundling multiple
215
+ * payment instructions.
216
+ */
217
+ interface BuildTransactionOptions {
218
+ /**
219
+ * Compute-unit limit attached to the transaction. Defaults to 200 000 -
220
+ * comfortable headroom for two SystemProgram transfers + a few extra ops.
221
+ */
222
+ computeUnitLimit?: number;
223
+ /**
224
+ * Per-CU priority-fee override in microLamports. When omitted, the
225
+ * strategy queries `getRecentPrioritizationFees`, sorts by percentile, and
226
+ * uses that value (cached for 10s). Pass an explicit value to skip the
227
+ * RPC call or override the percentile heuristic during traffic spikes.
228
+ */
229
+ priorityFeeMicroLamports?: bigint;
230
+ /**
231
+ * Percentile of the recent priority-fee distribution to charge when
232
+ * `priorityFeeMicroLamports` is not supplied. 50 = median, 75 = upper
233
+ * quartile (default), 90 = aggressive.
234
+ */
235
+ priorityFeePercentile?: number;
236
+ }
188
237
 
189
238
  declare class NostrPool {
190
239
  private pool;
@@ -424,7 +473,9 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
424
473
  createPaymentRequest(recipientAddress: string, amount: number, config: ProtocolConfigInput, options?: {
425
474
  expirySecs?: number;
426
475
  }): PaymentRequestData;
427
- validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string): PaymentValidationError | null;
476
+ validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string, options?: {
477
+ maxAmountLamports?: bigint;
478
+ }): PaymentValidationError | null;
428
479
  /**
429
480
  * Build, sign, and return a transaction for the supplied payment request.
430
481
  * The caller is responsible for sending it (e.g. via `rpc.sendTransaction`).
@@ -433,7 +484,7 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
433
484
  * read-only, non-signer account so providers can detect the payment via
434
485
  * `getSignaturesForAddress(reference)`.
435
486
  */
436
- buildTransaction(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput): Promise<Readonly<unknown>>;
487
+ buildTransaction(paymentRequest: PaymentRequestData, payerSigner: Signer, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput, options?: BuildTransactionOptions): Promise<Readonly<unknown>>;
437
488
  verifyPayment(rpc: Rpc<SolanaRpcApi>, paymentRequest: PaymentRequestData, config: ProtocolConfigInput, options?: VerifyOptions): Promise<VerifyResult>;
438
489
  private _verifyBySignature;
439
490
  private _verifyByReference;
@@ -448,7 +499,7 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
448
499
  * Caller is responsible for validating `paymentRequest` upstream;
449
500
  * `buildTransaction` already does that before invoking this helper.
450
501
  */
451
- declare function buildPaymentInstructions(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner): readonly unknown[];
502
+ declare function buildPaymentInstructions(paymentRequest: PaymentRequestData, payerSigner: Signer): readonly unknown[];
452
503
  /**
453
504
  * Convenience wrapper: fetch the on-chain protocol config first, then build a
454
505
  * payment request using its current fee/treasury values.
@@ -478,6 +529,101 @@ declare function validateExpiry(createdAt: number, expirySecs: number): string |
478
529
  /** Assert that payment request timestamps are valid and not expired. Throws on failure. */
479
530
  declare function assertExpiry(createdAt: number, expirySecs: number): void;
480
531
 
532
+ interface EstimatePriorityFeeOptions {
533
+ /**
534
+ * Percentile of the recent prioritization-fee distribution to charge.
535
+ * 50 = median, 75 = upper quartile, 90 = aggressive. Defaults to 75.
536
+ */
537
+ percentile?: number;
538
+ /**
539
+ * Cache window in milliseconds. Subsequent calls within this window with the
540
+ * same accounts will return the cached value. Defaults to 10s.
541
+ */
542
+ ttlMs?: number;
543
+ /**
544
+ * Optional account list passed to `getRecentPrioritizationFees` so the node
545
+ * returns fees observed on writes touching these accounts. Empty = global.
546
+ */
547
+ accounts?: readonly Address[];
548
+ }
549
+ /**
550
+ * Estimate a per-compute-unit priority fee from recent blocks, in
551
+ * microLamports (1 microLamport = 0.000001 Lamports).
552
+ *
553
+ * Falls back to a 1000 microLamport floor when the RPC returns no samples
554
+ * (typical on private clusters or under maintenance). Negative percentiles
555
+ * are clamped to the median.
556
+ *
557
+ * Cached per accounts-key for `ttlMs` (default 10s) using the same
558
+ * in-process cache pattern as `getProtocolConfig`.
559
+ */
560
+ declare function estimatePriorityFeeMicroLamports(rpc: Rpc<SolanaRpcApi>, options?: EstimatePriorityFeeOptions): Promise<bigint>;
561
+ declare function clearPriorityFeeCache(): void;
562
+ interface RecentPrioritizationFeeLike {
563
+ prioritizationFee: bigint | number;
564
+ slot?: bigint | number;
565
+ }
566
+ declare function pickPercentileFee(samples: readonly RecentPrioritizationFeeLike[], percentile: number): bigint;
567
+
568
+ /**
569
+ * Wire-shape for a NIP-90 payment_request blob, as parsed via JSON.parse.
570
+ *
571
+ * Stricter than the loose TypeScript interface: rejects negative amounts,
572
+ * floats, NaN/Infinity, mistyped recipient/reference, and any expiry
573
+ * outside `[1, LIMITS.MAX_TIMEOUT_SECS]`. The strategy applies semantic
574
+ * checks (recipient match, fee amount, expiry-vs-now) on top of this.
575
+ */
576
+ declare const PaymentRequestSchema: z.ZodObject<{
577
+ recipient: z.ZodString;
578
+ amount: z.ZodNumber;
579
+ reference: z.ZodString;
580
+ description: z.ZodOptional<z.ZodString>;
581
+ fee_address: z.ZodOptional<z.ZodString>;
582
+ fee_amount: z.ZodOptional<z.ZodNumber>;
583
+ created_at: z.ZodNumber;
584
+ expiry_secs: z.ZodNumber;
585
+ }, "strip", z.ZodTypeAny, {
586
+ recipient: string;
587
+ amount: number;
588
+ reference: string;
589
+ created_at: number;
590
+ expiry_secs: number;
591
+ description?: string | undefined;
592
+ fee_address?: string | undefined;
593
+ fee_amount?: number | undefined;
594
+ }, {
595
+ recipient: string;
596
+ amount: number;
597
+ reference: string;
598
+ created_at: number;
599
+ expiry_secs: number;
600
+ description?: string | undefined;
601
+ fee_address?: string | undefined;
602
+ fee_amount?: number | undefined;
603
+ }>;
604
+ type ParsedPaymentRequest = z.infer<typeof PaymentRequestSchema>;
605
+ interface ParseOptions {
606
+ /** Optional max amount cap (lamports). Rejects requests that exceed it. */
607
+ maxAmountLamports?: bigint;
608
+ }
609
+ interface ParseError {
610
+ code: 'invalid_json' | 'schema' | 'amount_exceeds_max';
611
+ message: string;
612
+ }
613
+ type ParseResult = {
614
+ ok: true;
615
+ data: ParsedPaymentRequest;
616
+ } | {
617
+ ok: false;
618
+ error: ParseError;
619
+ };
620
+ /**
621
+ * Parse a JSON-encoded payment request through the Zod schema, optionally
622
+ * enforcing a `maxAmountLamports` ceiling supplied by the caller (e.g. the
623
+ * customer's per-job spending cap).
624
+ */
625
+ declare function parsePaymentRequest(input: string, options?: ParseOptions): ParseResult;
626
+
481
627
  /**
482
628
  * Snapshot of the on-chain elisym-config program state.
483
629
  *
@@ -539,6 +685,82 @@ declare class BoundedSet<T> {
539
685
  add(item: T): void;
540
686
  }
541
687
 
688
+ /**
689
+ * Sliding-window rate limiter keyed by an arbitrary string (typically a
690
+ * customer pubkey). Each key gets at most `maxPerWindow` requests inside a
691
+ * rolling `windowMs`. Stale timestamps are pruned lazily on every `check`.
692
+ * When the tracked-key set grows past `maxKeys`, the least-recently-used
693
+ * key is evicted so an attacker cannot exhaust memory by cycling keys.
694
+ *
695
+ * Thread-safety: not required. Designed for single-threaded JS consumers
696
+ * (Node/Bun event loops, browser main thread). No timers - pruning happens
697
+ * inside `check` and `prune`.
698
+ */
699
+ interface SlidingWindowLimiterOptions {
700
+ /** Rolling window width, in ms. */
701
+ windowMs: number;
702
+ /** Max hits allowed per key inside the window. */
703
+ maxPerWindow: number;
704
+ /** Cap on total tracked keys. LRU-evicted past this cap. */
705
+ maxKeys: number;
706
+ }
707
+ interface RateLimitDecision {
708
+ allowed: boolean;
709
+ /** Wall-clock timestamp (ms) when the limit window will reset for this key. */
710
+ resetAt: number;
711
+ /** Number of hits inside the current window after this call (or the attempted hit if denied). */
712
+ count: number;
713
+ }
714
+ interface SlidingWindowLimiter {
715
+ /** Record a hit against `key`; return whether it was allowed. */
716
+ check(key: string, now?: number): RateLimitDecision;
717
+ /**
718
+ * Inspect the current state for `key` without recording a hit. Useful
719
+ * when a single request must clear multiple limiters and callers want
720
+ * to avoid double-counting against one limiter after another denies.
721
+ */
722
+ peek(key: string, now?: number): RateLimitDecision;
723
+ /** Drop entries whose windows have fully elapsed. Bounded memory hygiene. */
724
+ prune(now?: number): void;
725
+ /** Current number of tracked keys. */
726
+ size(): number;
727
+ /** Clear all state. */
728
+ reset(): void;
729
+ }
730
+ declare function createSlidingWindowLimiter(options: SlidingWindowLimiterOptions): SlidingWindowLimiter;
731
+
732
+ /**
733
+ * Canonical pino redact paths for the elisym stack. Plugin, CLI, MCP, and
734
+ * any downstream integrator should consume these arrays directly - one
735
+ * source of truth for "fields that carry user input or secrets".
736
+ *
737
+ * Wire them into a pino instance like:
738
+ *
739
+ * import pino from 'pino';
740
+ * import { DEFAULT_REDACT_PATHS, makeCensor } from '@elisym/sdk';
741
+ * const logger = pino({
742
+ * redact: { paths: DEFAULT_REDACT_PATHS, censor: makeCensor() },
743
+ * });
744
+ */
745
+ /**
746
+ * Field paths that carry Nostr / Solana secret keys or operator secrets.
747
+ * Censored as `[REDACTED]`.
748
+ */
749
+ declare const SECRET_REDACT_PATHS: string[];
750
+ /**
751
+ * Field paths that carry customer-confidential text (LLM prompts, raw
752
+ * event content, job input). Censored as `[INPUT REDACTED]` so log
753
+ * readers can distinguish redacted input from redacted secrets.
754
+ */
755
+ declare const INPUT_REDACT_PATHS: string[];
756
+ /**
757
+ * Union of the two arrays, in the order pino's redact engine should
758
+ * visit them. Prefer this when wiring a new logger so no downstream
759
+ * consumer forgets half the set.
760
+ */
761
+ declare const DEFAULT_REDACT_PATHS: string[];
762
+ declare function makeCensor(): (value: unknown, path: string[]) => string;
763
+
542
764
  declare const RELAYS: string[];
543
765
  declare const KIND_APP_HANDLER = 31990;
544
766
  declare const KIND_JOB_REQUEST_BASE = 5000;
@@ -614,4 +836,4 @@ declare const LIMITS: {
614
836
  readonly MAX_CAPABILITY_LENGTH: 64;
615
837
  };
616
838
 
617
- export { type Agent, BoundedSet, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type GetProtocolConfigOptions, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type PaymentInfo, type PaymentRequestData, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, nip44Decrypt, nip44Encrypt, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
839
+ export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Address, TransactionSigner, Rpc, SolanaRpcApi } from '@solana/kit';
2
2
  import { Filter, Event } from 'nostr-tools';
3
+ import { z } from 'zod';
3
4
 
4
5
  declare class ElisymIdentity {
5
6
  private _secretKey;
@@ -139,6 +140,18 @@ interface ElisymClientConfig {
139
140
  relays?: string[];
140
141
  }
141
142
 
143
+ /**
144
+ * Pluggable signer used by `PaymentStrategy.buildTransaction`.
145
+ *
146
+ * Aliased to Solana Kit's `TransactionSigner` so callers can pass a hot
147
+ * `KeyPairSigner`, an external KMS-backed signer, a hardware-wallet adapter,
148
+ * or any other implementation that conforms to the Solana Kit signer contract
149
+ * (TransactionPartialSigner / TransactionSendingSigner / TransactionModifyingSigner).
150
+ *
151
+ * Exposing the alias here lets downstream packages depend on `@elisym/sdk`'s
152
+ * abstraction instead of importing Kit directly when wiring custom signers.
153
+ */
154
+ type Signer = TransactionSigner;
142
155
  /**
143
156
  * Protocol fee + treasury inputs for building a payment request.
144
157
  *
@@ -173,18 +186,54 @@ interface PaymentStrategy {
173
186
  * Validate that a payment request has the correct recipient and protocol fee.
174
187
  * Returns a typed validation error if invalid, null if OK.
175
188
  */
176
- validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string): PaymentValidationError | null;
189
+ validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string, options?: {
190
+ maxAmountLamports?: bigint;
191
+ }): PaymentValidationError | null;
177
192
  /**
178
- * Build and sign a transaction from a payment request using a TransactionSigner.
179
- * Returns a chain-specific signed transaction value. The caller is responsible
180
- * for sending it (e.g. via `rpc.sendTransaction(...).send()`).
193
+ * Build and sign a transaction from a payment request using a `Signer`.
194
+ *
195
+ * The `Signer` parameter is intentionally the abstract interface, not a
196
+ * concrete `KeyPairSigner`, so callers can plug in external signers
197
+ * (KMS, hardware wallet, ElizaOS approval Action) without holding the
198
+ * raw secret key in process memory.
199
+ *
200
+ * Returns a chain-specific signed transaction value. The caller is
201
+ * responsible for sending it (e.g. via `rpc.sendTransaction(...).send()`).
181
202
  */
182
- buildTransaction(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput): Promise<unknown>;
203
+ buildTransaction(paymentRequest: PaymentRequestData, payerSigner: Signer, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput, options?: BuildTransactionOptions): Promise<unknown>;
183
204
  /**
184
205
  * Verify a payment on-chain.
185
206
  */
186
207
  verifyPayment(rpc: Rpc<SolanaRpcApi>, paymentRequest: PaymentRequestData, config: ProtocolConfigInput, options?: VerifyOptions): Promise<VerifyResult>;
187
208
  }
209
+ /**
210
+ * Optional knobs for `PaymentStrategy.buildTransaction`.
211
+ *
212
+ * Defaults are chosen for typical Solana mainnet conditions; override these
213
+ * when the caller knows peak fees are elevated, when running against a
214
+ * private cluster with no priority-fee samples, or when bundling multiple
215
+ * payment instructions.
216
+ */
217
+ interface BuildTransactionOptions {
218
+ /**
219
+ * Compute-unit limit attached to the transaction. Defaults to 200 000 -
220
+ * comfortable headroom for two SystemProgram transfers + a few extra ops.
221
+ */
222
+ computeUnitLimit?: number;
223
+ /**
224
+ * Per-CU priority-fee override in microLamports. When omitted, the
225
+ * strategy queries `getRecentPrioritizationFees`, sorts by percentile, and
226
+ * uses that value (cached for 10s). Pass an explicit value to skip the
227
+ * RPC call or override the percentile heuristic during traffic spikes.
228
+ */
229
+ priorityFeeMicroLamports?: bigint;
230
+ /**
231
+ * Percentile of the recent priority-fee distribution to charge when
232
+ * `priorityFeeMicroLamports` is not supplied. 50 = median, 75 = upper
233
+ * quartile (default), 90 = aggressive.
234
+ */
235
+ priorityFeePercentile?: number;
236
+ }
188
237
 
189
238
  declare class NostrPool {
190
239
  private pool;
@@ -424,7 +473,9 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
424
473
  createPaymentRequest(recipientAddress: string, amount: number, config: ProtocolConfigInput, options?: {
425
474
  expirySecs?: number;
426
475
  }): PaymentRequestData;
427
- validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string): PaymentValidationError | null;
476
+ validatePaymentRequest(requestJson: string, config: ProtocolConfigInput, expectedRecipient?: string, options?: {
477
+ maxAmountLamports?: bigint;
478
+ }): PaymentValidationError | null;
428
479
  /**
429
480
  * Build, sign, and return a transaction for the supplied payment request.
430
481
  * The caller is responsible for sending it (e.g. via `rpc.sendTransaction`).
@@ -433,7 +484,7 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
433
484
  * read-only, non-signer account so providers can detect the payment via
434
485
  * `getSignaturesForAddress(reference)`.
435
486
  */
436
- buildTransaction(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput): Promise<Readonly<unknown>>;
487
+ buildTransaction(paymentRequest: PaymentRequestData, payerSigner: Signer, rpc: Rpc<SolanaRpcApi>, config: ProtocolConfigInput, options?: BuildTransactionOptions): Promise<Readonly<unknown>>;
437
488
  verifyPayment(rpc: Rpc<SolanaRpcApi>, paymentRequest: PaymentRequestData, config: ProtocolConfigInput, options?: VerifyOptions): Promise<VerifyResult>;
438
489
  private _verifyBySignature;
439
490
  private _verifyByReference;
@@ -448,7 +499,7 @@ declare class SolanaPaymentStrategy implements PaymentStrategy {
448
499
  * Caller is responsible for validating `paymentRequest` upstream;
449
500
  * `buildTransaction` already does that before invoking this helper.
450
501
  */
451
- declare function buildPaymentInstructions(paymentRequest: PaymentRequestData, payerSigner: TransactionSigner): readonly unknown[];
502
+ declare function buildPaymentInstructions(paymentRequest: PaymentRequestData, payerSigner: Signer): readonly unknown[];
452
503
  /**
453
504
  * Convenience wrapper: fetch the on-chain protocol config first, then build a
454
505
  * payment request using its current fee/treasury values.
@@ -478,6 +529,101 @@ declare function validateExpiry(createdAt: number, expirySecs: number): string |
478
529
  /** Assert that payment request timestamps are valid and not expired. Throws on failure. */
479
530
  declare function assertExpiry(createdAt: number, expirySecs: number): void;
480
531
 
532
+ interface EstimatePriorityFeeOptions {
533
+ /**
534
+ * Percentile of the recent prioritization-fee distribution to charge.
535
+ * 50 = median, 75 = upper quartile, 90 = aggressive. Defaults to 75.
536
+ */
537
+ percentile?: number;
538
+ /**
539
+ * Cache window in milliseconds. Subsequent calls within this window with the
540
+ * same accounts will return the cached value. Defaults to 10s.
541
+ */
542
+ ttlMs?: number;
543
+ /**
544
+ * Optional account list passed to `getRecentPrioritizationFees` so the node
545
+ * returns fees observed on writes touching these accounts. Empty = global.
546
+ */
547
+ accounts?: readonly Address[];
548
+ }
549
+ /**
550
+ * Estimate a per-compute-unit priority fee from recent blocks, in
551
+ * microLamports (1 microLamport = 0.000001 Lamports).
552
+ *
553
+ * Falls back to a 1000 microLamport floor when the RPC returns no samples
554
+ * (typical on private clusters or under maintenance). Negative percentiles
555
+ * are clamped to the median.
556
+ *
557
+ * Cached per accounts-key for `ttlMs` (default 10s) using the same
558
+ * in-process cache pattern as `getProtocolConfig`.
559
+ */
560
+ declare function estimatePriorityFeeMicroLamports(rpc: Rpc<SolanaRpcApi>, options?: EstimatePriorityFeeOptions): Promise<bigint>;
561
+ declare function clearPriorityFeeCache(): void;
562
+ interface RecentPrioritizationFeeLike {
563
+ prioritizationFee: bigint | number;
564
+ slot?: bigint | number;
565
+ }
566
+ declare function pickPercentileFee(samples: readonly RecentPrioritizationFeeLike[], percentile: number): bigint;
567
+
568
+ /**
569
+ * Wire-shape for a NIP-90 payment_request blob, as parsed via JSON.parse.
570
+ *
571
+ * Stricter than the loose TypeScript interface: rejects negative amounts,
572
+ * floats, NaN/Infinity, mistyped recipient/reference, and any expiry
573
+ * outside `[1, LIMITS.MAX_TIMEOUT_SECS]`. The strategy applies semantic
574
+ * checks (recipient match, fee amount, expiry-vs-now) on top of this.
575
+ */
576
+ declare const PaymentRequestSchema: z.ZodObject<{
577
+ recipient: z.ZodString;
578
+ amount: z.ZodNumber;
579
+ reference: z.ZodString;
580
+ description: z.ZodOptional<z.ZodString>;
581
+ fee_address: z.ZodOptional<z.ZodString>;
582
+ fee_amount: z.ZodOptional<z.ZodNumber>;
583
+ created_at: z.ZodNumber;
584
+ expiry_secs: z.ZodNumber;
585
+ }, "strip", z.ZodTypeAny, {
586
+ recipient: string;
587
+ amount: number;
588
+ reference: string;
589
+ created_at: number;
590
+ expiry_secs: number;
591
+ description?: string | undefined;
592
+ fee_address?: string | undefined;
593
+ fee_amount?: number | undefined;
594
+ }, {
595
+ recipient: string;
596
+ amount: number;
597
+ reference: string;
598
+ created_at: number;
599
+ expiry_secs: number;
600
+ description?: string | undefined;
601
+ fee_address?: string | undefined;
602
+ fee_amount?: number | undefined;
603
+ }>;
604
+ type ParsedPaymentRequest = z.infer<typeof PaymentRequestSchema>;
605
+ interface ParseOptions {
606
+ /** Optional max amount cap (lamports). Rejects requests that exceed it. */
607
+ maxAmountLamports?: bigint;
608
+ }
609
+ interface ParseError {
610
+ code: 'invalid_json' | 'schema' | 'amount_exceeds_max';
611
+ message: string;
612
+ }
613
+ type ParseResult = {
614
+ ok: true;
615
+ data: ParsedPaymentRequest;
616
+ } | {
617
+ ok: false;
618
+ error: ParseError;
619
+ };
620
+ /**
621
+ * Parse a JSON-encoded payment request through the Zod schema, optionally
622
+ * enforcing a `maxAmountLamports` ceiling supplied by the caller (e.g. the
623
+ * customer's per-job spending cap).
624
+ */
625
+ declare function parsePaymentRequest(input: string, options?: ParseOptions): ParseResult;
626
+
481
627
  /**
482
628
  * Snapshot of the on-chain elisym-config program state.
483
629
  *
@@ -539,6 +685,82 @@ declare class BoundedSet<T> {
539
685
  add(item: T): void;
540
686
  }
541
687
 
688
+ /**
689
+ * Sliding-window rate limiter keyed by an arbitrary string (typically a
690
+ * customer pubkey). Each key gets at most `maxPerWindow` requests inside a
691
+ * rolling `windowMs`. Stale timestamps are pruned lazily on every `check`.
692
+ * When the tracked-key set grows past `maxKeys`, the least-recently-used
693
+ * key is evicted so an attacker cannot exhaust memory by cycling keys.
694
+ *
695
+ * Thread-safety: not required. Designed for single-threaded JS consumers
696
+ * (Node/Bun event loops, browser main thread). No timers - pruning happens
697
+ * inside `check` and `prune`.
698
+ */
699
+ interface SlidingWindowLimiterOptions {
700
+ /** Rolling window width, in ms. */
701
+ windowMs: number;
702
+ /** Max hits allowed per key inside the window. */
703
+ maxPerWindow: number;
704
+ /** Cap on total tracked keys. LRU-evicted past this cap. */
705
+ maxKeys: number;
706
+ }
707
+ interface RateLimitDecision {
708
+ allowed: boolean;
709
+ /** Wall-clock timestamp (ms) when the limit window will reset for this key. */
710
+ resetAt: number;
711
+ /** Number of hits inside the current window after this call (or the attempted hit if denied). */
712
+ count: number;
713
+ }
714
+ interface SlidingWindowLimiter {
715
+ /** Record a hit against `key`; return whether it was allowed. */
716
+ check(key: string, now?: number): RateLimitDecision;
717
+ /**
718
+ * Inspect the current state for `key` without recording a hit. Useful
719
+ * when a single request must clear multiple limiters and callers want
720
+ * to avoid double-counting against one limiter after another denies.
721
+ */
722
+ peek(key: string, now?: number): RateLimitDecision;
723
+ /** Drop entries whose windows have fully elapsed. Bounded memory hygiene. */
724
+ prune(now?: number): void;
725
+ /** Current number of tracked keys. */
726
+ size(): number;
727
+ /** Clear all state. */
728
+ reset(): void;
729
+ }
730
+ declare function createSlidingWindowLimiter(options: SlidingWindowLimiterOptions): SlidingWindowLimiter;
731
+
732
+ /**
733
+ * Canonical pino redact paths for the elisym stack. Plugin, CLI, MCP, and
734
+ * any downstream integrator should consume these arrays directly - one
735
+ * source of truth for "fields that carry user input or secrets".
736
+ *
737
+ * Wire them into a pino instance like:
738
+ *
739
+ * import pino from 'pino';
740
+ * import { DEFAULT_REDACT_PATHS, makeCensor } from '@elisym/sdk';
741
+ * const logger = pino({
742
+ * redact: { paths: DEFAULT_REDACT_PATHS, censor: makeCensor() },
743
+ * });
744
+ */
745
+ /**
746
+ * Field paths that carry Nostr / Solana secret keys or operator secrets.
747
+ * Censored as `[REDACTED]`.
748
+ */
749
+ declare const SECRET_REDACT_PATHS: string[];
750
+ /**
751
+ * Field paths that carry customer-confidential text (LLM prompts, raw
752
+ * event content, job input). Censored as `[INPUT REDACTED]` so log
753
+ * readers can distinguish redacted input from redacted secrets.
754
+ */
755
+ declare const INPUT_REDACT_PATHS: string[];
756
+ /**
757
+ * Union of the two arrays, in the order pino's redact engine should
758
+ * visit them. Prefer this when wiring a new logger so no downstream
759
+ * consumer forgets half the set.
760
+ */
761
+ declare const DEFAULT_REDACT_PATHS: string[];
762
+ declare function makeCensor(): (value: unknown, path: string[]) => string;
763
+
542
764
  declare const RELAYS: string[];
543
765
  declare const KIND_APP_HANDLER = 31990;
544
766
  declare const KIND_JOB_REQUEST_BASE = 5000;
@@ -614,4 +836,4 @@ declare const LIMITS: {
614
836
  readonly MAX_CAPABILITY_LENGTH: 64;
615
837
  };
616
838
 
617
- export { type Agent, BoundedSet, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type GetProtocolConfigOptions, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type PaymentInfo, type PaymentRequestData, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, nip44Decrypt, nip44Encrypt, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
839
+ export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };