@molpha/sdk 0.2.0-dev-20260911113923 → 0.2.0-dev-20260912150104

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/README.md CHANGED
@@ -370,6 +370,70 @@ Secrets are encrypted into per-node envelopes. The gateway coordinates the round
370
370
 
371
371
  Private API access is still an active security-sensitive surface. Do not treat encrypted secret delivery as production-ready until gateway/node-side test vectors and validation are complete.
372
372
 
373
+ ## Paywalled API sources
374
+
375
+ Some API sources answer an unpaid request with HTTP 402 instead of data. You pay such a
376
+ source directly, from your own wallet on the source's network. Molpha never holds, signs
377
+ for, or converts those funds, and the Molpha side of the round is unchanged: a paid
378
+ source still costs exactly one round of subscription quota.
379
+
380
+ ```ts
381
+ import { createEvmSignerFromPrivateKey } from "@molpha/sdk";
382
+
383
+ const result = await sdk.gateway.requestSignedData({
384
+ apiConfig,
385
+ signaturesRequired,
386
+ sourcePayment: {
387
+ signer: createEvmSignerFromPrivateKey(process.env.EVM_PRIVATE_KEY!),
388
+ },
389
+ });
390
+ ```
391
+
392
+ The SDK fetches the source unpaid to read its terms, signs one EIP-3009 transfer
393
+ authorization per node in the round's eligible set, and sends them as `sourcePayments`.
394
+ Beta signs `exact` payments in USDC on Base and Base Sepolia only; any other network or
395
+ asset is rejected before you sign anything. Private keys stay in your process — pass your
396
+ own `EvmSigner` to keep them in a wallet or KMS instead.
397
+
398
+ **You pay per node fetch, not per datum.** Independent fetching is the point of the
399
+ protocol, so one round costs up to
400
+ `min(signaturesRequired + redundancyBuffer, nodeCount)` source calls — the same eligible
401
+ set the selection bitmap is drawn from. Only authorizations a node actually spends ever
402
+ settle, so unused ones cost nothing. Sign the whole set anyway: a short set starves buffer
403
+ nodes and makes the round more likely to fail.
404
+
405
+ Each retry signs fresh authorizations with new nonces, because a dispatched round cannot
406
+ be replayed and a retry is therefore a new round. Authorizations the source already
407
+ settled stay spent if the round then fails; unsettled ones expire worthless. There is no
408
+ refund path and none is needed.
409
+
410
+ Source payment is per-round access material, like a credential. It never enters
411
+ `sourceId`, so a paid and an unpaid fetch of the same API config share one source
412
+ identity.
413
+
414
+ ### Without a source wallet
415
+
416
+ A paywalled source with no `sourcePayment` throws `UpstreamPaymentRequiredError`, whose
417
+ `quote` carries the resource, the eligible set size, and the source's own rejection
418
+ detail. This is terminal — the round is never retried blindly.
419
+
420
+ ```ts
421
+ import { UpstreamPaymentRequiredError } from "@molpha/sdk";
422
+
423
+ try {
424
+ await sdk.gateway.requestSignedData({ apiConfig, signaturesRequired });
425
+ } catch (err) {
426
+ if (err instanceof UpstreamPaymentRequiredError) {
427
+ console.log(`${err.quote.resource} needs ${err.quote.eligibleSetSize} paid fetches`);
428
+ }
429
+ }
430
+ ```
431
+
432
+ To drive the payment yourself, `probeSource`, `signSourcePayments` and `eligibleSetSize`
433
+ are exported for x402-native agents calling `/v1/x402/execute` with their own client.
434
+ `gateway.getNodesInfo()` reports the gateway's advisory view of the registry policy that
435
+ sizes the eligible set, for callers with no Solana connection.
436
+
373
437
  ## EVM verification
374
438
 
375
439
  After a gateway round, the same signed result can be verified on EVM chains.
@@ -621,12 +685,15 @@ Current scope:
621
685
  - gateway signed-data requests (failover, retries, per-gateway request auth, context cache);
622
686
  - Solana attestation submission and feed/registry reads;
623
687
  - private API encryption helpers (pre-production);
688
+ - caller-funded x402 payments for paywalled API sources (Base USDC, pre-production);
624
689
  - EVM and Starknet verifier argument building;
625
690
  - deployed testnet verifier address helpers.
626
691
 
627
692
  Known limitations:
628
693
 
629
694
  - private API envelope encryption still needs gateway/node-side test-vector validation;
695
+ - paid-source payments sign `exact` USDC on Base and Base Sepolia only, and are pending
696
+ end-to-end validation against a live paywalled source;
630
697
  - verifier-node registration and admin tooling are intentionally outside this package;
631
698
  - production deployments should use authenticated gateway requests;
632
699
  - testnet verifier addresses may change between protocol releases.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Address } from '@solana/kit';
2
2
  import { Wallet, Idl, AnchorProvider } from '@anchor-lang/core';
3
- import { R as RegistrySelectionConfig, S as Signer, N as NodeKeyVerifier, a as Node, A as APIConfig, D as DataUpdateResult, b as SolanaAccountMeta, c as SolanaConnection, d as SolanaAddress, e as NodeKeyVerifierArgs, M as MolphaWallet } from './wallet-udLZ5Iy0.js';
4
- export { E as EncKeyBundle, f as SchnorrSignature, g as gatewaySignerFromWallet, s as signerFromKeypair } from './wallet-udLZ5Iy0.js';
3
+ import { U as UpstreamQuote, A as APIConfig, a as AssetDomain, b as UpstreamTerms, E as EvmSigner, R as RegistrySelectionConfig, S as Signer, N as NodeKeyVerifier, c as Node, d as NodesInfo, e as SourcePaymentOptions, D as DataUpdateResult, f as SolanaAccountMeta, g as SolanaConnection, h as SolanaAddress, i as NodeKeyVerifierArgs, M as MolphaWallet } from './wallet-B3mYmC2b.js';
4
+ export { j as EncKeyBundle, k as RegistryInfo, l as SchnorrSignature, m as gatewaySignerFromWallet, s as signerFromKeypair } from './wallet-B3mYmC2b.js';
5
5
  import BN from 'bn.js';
6
6
 
7
7
  /**
@@ -56,6 +56,48 @@ declare function encodeRequestAuth(fields: RequestAuthFields): Uint8Array;
56
56
  /** `keccak256(REQUEST_AUTH_DOMAIN || encodeRequestAuth(fields))` — the message the consumer signs. */
57
57
  declare function hashRequestAuth(fields: RequestAuthFields): Uint8Array;
58
58
 
59
+ /** Raised when a round needs source payment the caller cannot or did not make. */
60
+ declare class UpstreamPaymentRequiredError extends Error {
61
+ readonly quote: UpstreamQuote;
62
+ constructor(quote: UpstreamQuote, message?: string);
63
+ }
64
+ /**
65
+ * Number of source fetches this round may perform, and so authorizations to
66
+ * sign: `min(signaturesRequired + redundancyBuffer, nodeCount)`. Identical to
67
+ * the round's selection size — the eligible set *is* the set that fetches.
68
+ */
69
+ declare function eligibleSetSize(signaturesRequired: number, registry: {
70
+ nodeCount: number;
71
+ redundancyBuffer: number;
72
+ }): number;
73
+ /** Reads a gateway 402 body and returns the relayed upstream quote, if any. */
74
+ declare function parseUpstreamQuote(envelope: unknown): UpstreamQuote | null;
75
+ /**
76
+ * Validates caller-supplied terms with the same allowlist and structural checks
77
+ * as {@link probeSource}. Returns canonical {@link UpstreamTerms} derived from
78
+ * `terms.requirements`, not the caller's top-level fields.
79
+ */
80
+ declare function validateSuppliedTerms(terms: UpstreamTerms, resource: string, assetDomain?: AssetDomain): UpstreamTerms;
81
+ /**
82
+ * Fetch the source unpaid to read its own x402 terms. This is a price fetch,
83
+ * not a data fetch: nodes remain the only fetchers of the value itself.
84
+ * Returns `null` when the source is not paywalled.
85
+ */
86
+ declare function probeSource(apiConfig: APIConfig, options?: {
87
+ secrets?: Record<string, string>;
88
+ assetDomain?: AssetDomain;
89
+ timeoutMs?: number;
90
+ }): Promise<UpstreamTerms | null>;
91
+ /**
92
+ * Sign one x402 payment authorization per eligible node.
93
+ *
94
+ * Sign the whole eligible set. Only authorizations a node actually spends ever
95
+ * settle, so unused ones cost nothing, while signing fewer starves buffer nodes
96
+ * and raises the round's failure probability. Every authorization carries a
97
+ * unique nonce, which is what prevents one from settling twice.
98
+ */
99
+ declare function signSourcePayments(terms: UpstreamTerms, signer: EvmSigner, count: number): Promise<string[]>;
100
+
59
101
  interface RequestSignedDataOptions {
60
102
  /**
61
103
  * The API definition to resolve. Its canonical form determines the round's
@@ -85,6 +127,17 @@ interface RequestSignedDataOptions {
85
127
  encrypt?: {
86
128
  secrets: Record<string, string>;
87
129
  };
130
+ /**
131
+ * Pays an API source that is itself x402-paywalled. The SDK reads the source's
132
+ * own 402, signs one authorization per node in the round's eligible set, and
133
+ * sends them with the round as `sourcePayments`.
134
+ *
135
+ * Payment goes to the source, never to Molpha: the round still costs exactly
136
+ * one round of subscription quota. Only authorizations a node actually spends
137
+ * ever settle, so the redundancy buffer costs nothing when it goes unused.
138
+ * Without this, a paywalled source throws {@link UpstreamPaymentRequiredError}.
139
+ */
140
+ sourcePayment?: SourcePaymentOptions;
88
141
  /** Max accepted value age in seconds. Default 60. */
89
142
  maxAge?: number;
90
143
  /** Each retry re-rolls the timestamp. Default 15. */
@@ -171,6 +224,16 @@ declare class MolphaGateway {
171
224
  defaultSubscriptionOwnerOrOptions?: string | MolphaGatewayOptions, defaultConsumerAuthority?: string);
172
225
  /** Tries endpoints in order; returns the first node list it can fetch. */
173
226
  getNodes(): Promise<Node[]>;
227
+ /**
228
+ * `GET /v1/nodes` — the peer set plus the gateway's view of the registry
229
+ * selection policy, which sizes the eligible set a paid source must fund.
230
+ *
231
+ * The `registry` block is advisory: it is whatever the gateway read from the
232
+ * chain, and is absent on older gateways or when that read failed. Prefer the
233
+ * on-chain read (`MolphaSolanaClient.getRegistrySelectionConfig`) whenever a
234
+ * Solana connection is available — `requestSignedData` already does.
235
+ */
236
+ getNodesInfo(): Promise<NodesInfo>;
174
237
  isHealthy(): Promise<boolean>;
175
238
  /**
176
239
  * `GET {url}/v1/info` — the gateway's on-chain identity. Throws when the gateway
@@ -202,6 +265,10 @@ declare class MolphaGateway {
202
265
  * read does not report `nodeCount`). Supply `opts.context` (e.g. from
203
266
  * {@link prepareContext}) to reuse cached inputs and skip those fetches — a
204
267
  * fully-populated context collapses the call to a single POST round.
268
+ *
269
+ * When the API source is itself x402-paywalled, pass `opts.sourcePayment` to
270
+ * fund it; without that, such a source throws
271
+ * {@link UpstreamPaymentRequiredError} carrying the gateway's quote.
205
272
  */
206
273
  requestSignedData(opts: RequestSignedDataOptions): Promise<DataUpdateResult>;
207
274
  /**
@@ -483,6 +550,13 @@ declare function bigIntFromBytesBe(bytes: Uint8Array): bigint;
483
550
  declare function ensureLength(bytes: Uint8Array, len: number, label: string): Uint8Array;
484
551
  /** Coerce hex string or bytes to a fixed-length byte array. */
485
552
  declare function toFixedBytes(value: string | Uint8Array, len: number, label: string): Uint8Array;
553
+ /**
554
+ * Standard base64 (padded, no line breaks). Isomorphic: `btoa` is available in
555
+ * Node 20+ and every browser, so no `Buffer` is pulled into browser bundles.
556
+ */
557
+ declare function bytesToBase64(bytes: Uint8Array): string;
558
+ /** Accepts padded or unpadded standard base64. */
559
+ declare function base64ToBytes(value: string): Uint8Array;
486
560
 
487
561
  /** `keccak256("MOLPHA_MESSAGE_V1")` domain separator. */
488
562
  declare const MESSAGE_PREFIX: Uint8Array;
@@ -601,6 +675,40 @@ type MolphaEvmNetwork = "evm-sepolia" | "arbitrum-sepolia" | "avalanche-fuji" |
601
675
  /** CREATE2 Molpha verifier address — identical on all supported EVM chains. */
602
676
  declare const MOLPHA_VERIFIER_ADDRESS: "0xE1fd792b7E54e0C8F0Cd1c8055E446ff36d233eB";
603
677
 
678
+ /** EIP-712 domain of the token contract that will settle the authorization. */
679
+ interface Eip712Domain {
680
+ name: string;
681
+ version: string;
682
+ chainId: number;
683
+ /** Token contract address, 0x-prefixed. */
684
+ verifyingContract: string;
685
+ }
686
+ /** EIP-3009 transfer authorization. Amounts are decimal strings of base units. */
687
+ interface TransferAuthorization {
688
+ from: string;
689
+ to: string;
690
+ value: string;
691
+ validAfter: string;
692
+ validBefore: string;
693
+ /** 0x-prefixed 32-byte nonce. */
694
+ nonce: string;
695
+ }
696
+ declare const EIP712_DOMAIN_TYPEHASH: Uint8Array<ArrayBufferLike>;
697
+ declare const TRANSFER_WITH_AUTHORIZATION_TYPEHASH: Uint8Array<ArrayBufferLike>;
698
+ declare function domainSeparator(domain: Eip712Domain): Uint8Array;
699
+ declare function transferWithAuthorizationHash(auth: TransferAuthorization): Uint8Array;
700
+ /** The 32-byte digest a wallet signs: `keccak256(0x1901 || domain || struct)`. */
701
+ declare function transferWithAuthorizationDigest(domain: Eip712Domain, auth: TransferAuthorization): Uint8Array;
702
+ /** EIP-55 checksum form. Some facilitators reject non-checksummed addresses. */
703
+ declare function toChecksumAddress(address: string): string;
704
+ /** The EIP-55 address of a raw secp256k1 private key. */
705
+ declare function evmAddressFromPrivateKey(privateKey: string | Uint8Array): string;
706
+ /**
707
+ * Build an {@link EvmSigner} from a raw secp256k1 private key. The key stays in
708
+ * the caller's process: neither Molpha nor the gateway ever sees one.
709
+ */
710
+ declare function createEvmSignerFromPrivateKey(privateKey: string | Uint8Array): EvmSigner;
711
+
604
712
  /** `(bytes32 sourceId, uint32 registryVersion, uint32 signaturesRequired, bytes32 valuePacked, uint64 timestamp)` */
605
713
  type EvmDataUpdateTuple = readonly [
606
714
  sourceId: `0x${string}`,
@@ -748,4 +856,4 @@ declare class MolphaSDK {
748
856
  }>;
749
857
  }
750
858
 
751
- export { APIConfig, type AttestationMessageFields, DEFAULT_GATEWAY_ENDPOINT, DataUpdateResult, type EvmDataUpdateTuple, type EvmSchnorrSignatureTuple, type EvmVerifierArgs, type FeedAccount, type GatewayEndpoint, type GatewayEndpointInput, GatewayError, type GatewayInfo, MESSAGE_PREFIX, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, type MolphaEvmNetwork, MolphaGateway, type MolphaGatewayOptions, MolphaSDK, type MolphaSDKOptions, MolphaSolanaClient, type MolphaStarknetNetwork, MolphaWallet, Node, NodeKeyVerifier, NodeKeyVerifierArgs, type PlanId, type PlanInfo, PlanType, REQUEST_AUTH_DOMAIN, RegistrySelectionConfig, type RegistryStateView, type RegistryView, type RequestAuthFields, type RequestSignedDataOptions, type RoundContext, Signer, type StarknetDataUpdate, type StarknetSchnorrSignature, type StarknetVerifierArgs, type SubmitAttestationArgs, type SubmitResult, type SubscribeResult, type SubscriptionInfo, addressToBytes, attestationMessageHash, attestationMessageHashFromResult, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, commitmentAddressToStarknetFelt, concatBytes, deriveApiConfigHash, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, effectiveSelectionSize, encodeRequestAuth, ensureLength, feedPda, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseGatewayInfo, planIdFromVariant, planPda, planVariant, protocolConfigPda, registryPda, registryStatePda, resolveRemainingAccounts, secp256k1PublicKeyFromCoordinates, selectedIndices, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, subscriptionPda, toFixedBytes, toFixedHex, u256beFromBigInt, u32be, u32le, u64be, u64le, utf8 };
859
+ export { APIConfig, AssetDomain, type AttestationMessageFields, DEFAULT_GATEWAY_ENDPOINT, DataUpdateResult, EIP712_DOMAIN_TYPEHASH, type Eip712Domain, type EvmDataUpdateTuple, type EvmSchnorrSignatureTuple, EvmSigner, type EvmVerifierArgs, type FeedAccount, type GatewayEndpoint, type GatewayEndpointInput, GatewayError, type GatewayInfo, MESSAGE_PREFIX, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, type MolphaEvmNetwork, MolphaGateway, type MolphaGatewayOptions, MolphaSDK, type MolphaSDKOptions, MolphaSolanaClient, type MolphaStarknetNetwork, MolphaWallet, Node, NodeKeyVerifier, NodeKeyVerifierArgs, NodesInfo, type PlanId, type PlanInfo, PlanType, REQUEST_AUTH_DOMAIN, RegistrySelectionConfig, type RegistryStateView, type RegistryView, type RequestAuthFields, type RequestSignedDataOptions, type RoundContext, Signer, SourcePaymentOptions, type StarknetDataUpdate, type StarknetSchnorrSignature, type StarknetVerifierArgs, type SubmitAttestationArgs, type SubmitResult, type SubscribeResult, type SubscriptionInfo, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, type TransferAuthorization, UpstreamPaymentRequiredError, UpstreamQuote, UpstreamTerms, addressToBytes, attestationMessageHash, attestationMessageHashFromResult, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, commitmentAddressToStarknetFelt, concatBytes, createEvmSignerFromPrivateKey, deriveApiConfigHash, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeRequestAuth, ensureLength, evmAddressFromPrivateKey, feedPda, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseGatewayInfo, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, resolveRemainingAccounts, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, subscriptionPda, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, utf8, validateSuppliedTerms };