@molpha/sdk 0.2.0-dev-20260911175245 → 0.2.0-dev-20260913100330

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.
@@ -514,40 +578,100 @@ const sepoliaDirect = MOLPHA_VERIFIER_STARKNET_SEPOLIA;
514
578
 
515
579
  ### Build verifier arguments
516
580
 
581
+ The verifier's entrypoint is `verify(attestation: Attestation, max_age: u64) -> (bool, u8)`.
582
+
517
583
  ```ts
518
584
  import { buildStarknetVerifierArgs } from "@molpha/sdk";
519
585
 
520
586
  const result = await sdk.gateway.requestSignedData({ apiConfig, signaturesRequired });
521
587
 
522
- const { dataUpdate, signature } = buildStarknetVerifierArgs(result);
588
+ const { attestation, maxAge } = buildStarknetVerifierArgs(result, { maxAge: 300 });
523
589
  ```
524
590
 
525
- The generated objects match the Molpha Starknet verifier interface (the Cairo struct still names the first field `feed_id`; positional calldata is unchanged):
591
+ `maxAge` is required. It is the freshness window in seconds: the verifier reports an older
592
+ attestation as `STALE`. `0` disables the check entirely — pass it only when your contract
593
+ enforces freshness or ordering itself, because a stateless verifier otherwise accepts a
594
+ correctly signed attestation forever.
595
+
596
+ The generated object matches the Cairo `Attestation` struct. Member order is Cairo `Serde`
597
+ order (and the signed message's order), so it differs from `DataUpdateResult`:
526
598
 
527
599
  ```ts
528
- // dataUpdate:
529
- // {
530
- // source_id: u256,
531
- // registry_version: u32,
532
- // signatures_required: u32,
533
- // value: u256,
534
- // canonical_timestamp: u64,
535
- // }
600
+ attestation:
601
+ {
602
+ payload: {
603
+ value: u256,
604
+ source_id: u256,
605
+ registry_version: u32,
606
+ signatures_required: u8,
607
+ canonical_timestamp: u64,
608
+ },
609
+ signature: {
610
+ signature: u256,
611
+ commitment: felt252, // 20-byte address as felt
612
+ signers_bitmap: u256,
613
+ },
614
+ }
615
+ ```
536
616
 
537
- // signature:
538
- // {
539
- // signature: u256,
540
- // commitment: felt252, // EVM-style 20-byte address as felt
541
- // signers_bitmap: u256,
542
- // }
617
+ The builder range-checks every integer against its Cairo type. Out-of-range calldata never
618
+ reaches `verify`: Cairo `Serde` fails while decoding the arguments and the call reverts
619
+ instead of returning a result code.
620
+
621
+ ### Call `verify` and read the result
622
+
623
+ `encodeStarknetVerifyCalldata` flattens the arguments into the 13 felts a raw `starknet_call`
624
+ takes, and `parseStarknetVerifyResult` decodes the `(bool, u8)` it returns. Any Starknet
625
+ client works; with `starknet.js`:
626
+
627
+ ```ts
628
+ import { RpcProvider } from "starknet";
629
+ import {
630
+ buildStarknetVerifierArgs,
631
+ encodeStarknetVerifyCalldata,
632
+ parseStarknetVerifyResult,
633
+ } from "@molpha/sdk";
634
+
635
+ const provider = new RpcProvider({ nodeUrl: STARKNET_RPC_URL });
636
+ const args = buildStarknetVerifierArgs(result, { maxAge: 300 });
637
+
638
+ const response = await provider.callContract({
639
+ contractAddress: verifierAddress,
640
+ entrypoint: "verify",
641
+ calldata: encodeStarknetVerifyCalldata(args),
642
+ });
643
+
644
+ const { success, code, reason } = parseStarknetVerifyResult(response);
645
+ // { success: true, code: 0, reason: "OK" }
646
+ // { success: false, code: 10, reason: "STALE" }
543
647
  ```
544
648
 
649
+ `verify` never reverts on well-formed calldata; a rejection is a result code. The codes are
650
+ shared with the EVM verifier contract and exported as `VERIFY_CODES`:
651
+
652
+ | Code | Name | Meaning |
653
+ |---|---|---|
654
+ | 0 | `OK` | Verified |
655
+ | 2 | `BAD_REGISTRY_VERSION` | `registryVersion` does not exist on this verifier |
656
+ | 3 | `MALFORMED` | Structurally invalid input, or dated in the future when `maxAge != 0` |
657
+ | 4 | `NOT_YET_ACTIVE` | `canonicalTimestamp` predates the registry version's activation |
658
+ | 5 | `VERSION_EXPIRED` | Registry version superseded more than the grace window earlier |
659
+ | 7 | `BAD_QUORUM` | Signers are not within the round's derived selection group |
660
+ | 8 | `BAD_AGGREGATE` | The signers' aggregate key is the point at infinity |
661
+ | 9 | `BAD_SIGNATURE` | The aggregate Schnorr signature does not verify |
662
+ | 10 | `STALE` | Older than `maxAge` |
663
+
664
+ Codes 1 and 6 are reserved and never returned. Codes are append-only, so
665
+ `parseStarknetVerifyResult` reports a code newer than your SDK as `reason: "UNKNOWN"` rather
666
+ than throwing.
667
+
545
668
  Lower-level helpers are also exported:
546
669
 
547
670
  ```ts
548
671
  import {
549
672
  commitmentAddressToStarknetFelt,
550
673
  signersBitmapToStarknetUint256,
674
+ verifyCodeName,
551
675
  } from "@molpha/sdk";
552
676
  ```
553
677
 
@@ -621,12 +745,15 @@ Current scope:
621
745
  - gateway signed-data requests (failover, retries, per-gateway request auth, context cache);
622
746
  - Solana attestation submission and feed/registry reads;
623
747
  - private API encryption helpers (pre-production);
624
- - EVM and Starknet verifier argument building;
748
+ - caller-funded x402 payments for paywalled API sources (Base USDC, pre-production);
749
+ - EVM and Starknet verifier argument building, Starknet `verify` calldata encoding and result decoding;
625
750
  - deployed testnet verifier address helpers.
626
751
 
627
752
  Known limitations:
628
753
 
629
754
  - private API envelope encryption still needs gateway/node-side test-vector validation;
755
+ - paid-source payments sign `exact` USDC on Base and Base Sepolia only, and are pending
756
+ end-to-end validation against a live paywalled source;
630
757
  - verifier-node registration and admin tooling are intentionally outside this package;
631
758
  - production deployments should use authenticated gateway requests;
632
759
  - testnet verifier addresses may change between protocol releases.
@@ -649,6 +776,9 @@ Solana paths such as selection bitmap and `submit_attestation` remaining-account
649
776
  | `result.feedId` / `NodeKeyVerifierArgs.feedId` | `.sourceId` |
650
777
  | EVM tuple `feedId`, ABI `jobId` | `sourceId` |
651
778
  | Starknet `feed_id` | `source_id` |
779
+ | `buildStarknetVerifierArgs(result)` → `{ dataUpdate, signature }` | `buildStarknetVerifierArgs(result, { maxAge })` → `{ attestation, maxAge }` for `verify(attestation, max_age)` |
780
+ | `StarknetDataUpdate` (`signatures_required: u32`) | `StarknetAttestationPayload` (`signatures_required: u8`, `value` first), nested in `StarknetAttestation` |
781
+ | Starknet `verify` returns `bool` | returns `(bool, u8)` — decode with `parseStarknetVerifyResult` |
652
782
  | `resolveRegistryIndexForVersion`, `VIRTUAL_INDEX`, `nodePda(index)` | removed — signer accounts are `registry.nodes[bit]`; `nodePda(owner)` |
653
783
 
654
784
  ## Develop
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-DwdBZgl1.js';
4
+ export { j as EncKeyBundle, k as RegistryInfo, l as SchnorrSignature, m as gatewaySignerFromWallet, s as signerFromKeypair } from './wallet-DwdBZgl1.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;
@@ -534,6 +608,45 @@ declare function deriveGroupBitmap(seed: Uint8Array, nodeCount: number, groupSiz
534
608
  */
535
609
  declare function deriveSelectionBitmap(sourceId: Uint8Array, registryVersion: number, nodeCount: number, signaturesRequired: number, redundancy?: number, ts?: number | bigint): Uint8Array;
536
610
 
611
+ /**
612
+ * Result codes returned by Molpha's stateless verifier contracts (`verify` on EVM and
613
+ * Starknet). Shared across VMs: the same attestation yields the same code on every chain.
614
+ *
615
+ * Append-only — never renumber. Mirrors `VerifyCodes.sol` in `molpha-core-contracts` and
616
+ * `verify_codes.cairo` in `molpha-starknet`. Two codes belong to mechanisms the verifiers
617
+ * do not implement; they are kept so the numbering can never shift under them.
618
+ */
619
+ declare const VERIFY_CODES: {
620
+ readonly OK: 0;
621
+ /** Reserved. Never returned. */
622
+ readonly FEED_WITNESS: 1;
623
+ /** The payload's `registryVersion` does not exist on this verifier. */
624
+ readonly BAD_REGISTRY_VERSION: 2;
625
+ /** Structurally invalid input, or a `canonicalTimestamp` in the future when `maxAge != 0`. */
626
+ readonly MALFORMED: 3;
627
+ /** `canonicalTimestamp` predates the registry version's activation. */
628
+ readonly NOT_YET_ACTIVE: 4;
629
+ /** The registry version was superseded more than the grace window before `canonicalTimestamp`. */
630
+ readonly VERSION_EXPIRED: 5;
631
+ /** Reserved. Never returned. */
632
+ readonly COMPROMISED_QUORUM: 6;
633
+ /** The signer set is not within the round's derived selection group. */
634
+ readonly BAD_QUORUM: 7;
635
+ /** The signers' aggregate public key is the point at infinity. */
636
+ readonly BAD_AGGREGATE: 8;
637
+ /** The aggregate Schnorr signature does not verify. */
638
+ readonly BAD_SIGNATURE: 9;
639
+ /** Older than the caller's `maxAge`. */
640
+ readonly STALE: 10;
641
+ };
642
+ type VerifyCodeName = keyof typeof VERIFY_CODES;
643
+ type VerifyCode = (typeof VERIFY_CODES)[VerifyCodeName];
644
+ /**
645
+ * Name of a verifier result code, or `undefined` for a code this SDK version does not know.
646
+ * Codes are append-only, so an unknown code is a newer verifier, not a malformed response.
647
+ */
648
+ declare function verifyCodeName(code: number): VerifyCodeName | undefined;
649
+
537
650
  /**
538
651
  * Minimal Molpha verifier ABI for `verify` and registry reads.
539
652
  * Matches the deployed `verify(DataUpdate, SchnorrSignature)` verifier. The first tuple
@@ -601,6 +714,40 @@ type MolphaEvmNetwork = "evm-sepolia" | "arbitrum-sepolia" | "avalanche-fuji" |
601
714
  /** CREATE2 Molpha verifier address — identical on all supported EVM chains. */
602
715
  declare const MOLPHA_VERIFIER_ADDRESS: "0xE1fd792b7E54e0C8F0Cd1c8055E446ff36d233eB";
603
716
 
717
+ /** EIP-712 domain of the token contract that will settle the authorization. */
718
+ interface Eip712Domain {
719
+ name: string;
720
+ version: string;
721
+ chainId: number;
722
+ /** Token contract address, 0x-prefixed. */
723
+ verifyingContract: string;
724
+ }
725
+ /** EIP-3009 transfer authorization. Amounts are decimal strings of base units. */
726
+ interface TransferAuthorization {
727
+ from: string;
728
+ to: string;
729
+ value: string;
730
+ validAfter: string;
731
+ validBefore: string;
732
+ /** 0x-prefixed 32-byte nonce. */
733
+ nonce: string;
734
+ }
735
+ declare const EIP712_DOMAIN_TYPEHASH: Uint8Array<ArrayBufferLike>;
736
+ declare const TRANSFER_WITH_AUTHORIZATION_TYPEHASH: Uint8Array<ArrayBufferLike>;
737
+ declare function domainSeparator(domain: Eip712Domain): Uint8Array;
738
+ declare function transferWithAuthorizationHash(auth: TransferAuthorization): Uint8Array;
739
+ /** The 32-byte digest a wallet signs: `keccak256(0x1901 || domain || struct)`. */
740
+ declare function transferWithAuthorizationDigest(domain: Eip712Domain, auth: TransferAuthorization): Uint8Array;
741
+ /** EIP-55 checksum form. Some facilitators reject non-checksummed addresses. */
742
+ declare function toChecksumAddress(address: string): string;
743
+ /** The EIP-55 address of a raw secp256k1 private key. */
744
+ declare function evmAddressFromPrivateKey(privateKey: string | Uint8Array): string;
745
+ /**
746
+ * Build an {@link EvmSigner} from a raw secp256k1 private key. The key stays in
747
+ * the caller's process: neither Molpha nor the gateway ever sees one.
748
+ */
749
+ declare function createEvmSignerFromPrivateKey(privateKey: string | Uint8Array): EvmSigner;
750
+
604
751
  /** `(bytes32 sourceId, uint32 registryVersion, uint32 signaturesRequired, bytes32 valuePacked, uint64 timestamp)` */
605
752
  type EvmDataUpdateTuple = readonly [
606
753
  sourceId: `0x${string}`,
@@ -643,31 +790,100 @@ declare const MOLPHA_VERIFIER_STARKNET_ADDRESSES: Record<MolphaStarknetNetwork,
643
790
  /** Resolve the deployed verifier address for a supported Starknet network. */
644
791
  declare function getMolphaStarknetVerifierAddress(network: MolphaStarknetNetwork): `0x${string}`;
645
792
 
646
- /** Starknet calldata shape for `DataUpdate`. */
647
- interface StarknetDataUpdate {
648
- /** 32-byte source id as u256 (the Cairo struct still names this field `feed_id`). */
793
+ /**
794
+ * Starknet calldata shape for `AttestationPayload`.
795
+ *
796
+ * Member order is the order of the signed message preimage and of Cairo `Serde`, so it is
797
+ * load bearing — it is not the order of `DataUpdateResult`.
798
+ */
799
+ interface StarknetAttestationPayload {
800
+ /** 32-byte packed value as `u256`. */
801
+ value: bigint;
802
+ /** 32-byte source id as `u256`. */
649
803
  source_id: bigint;
804
+ /** `u32`. */
650
805
  registry_version: number;
806
+ /** `u8`. */
651
807
  signatures_required: number;
652
- value: bigint;
808
+ /** `u64`, unix seconds. */
653
809
  canonical_timestamp: number;
654
810
  }
655
811
  /** Starknet calldata shape for `SchnorrSignature`. */
656
812
  interface StarknetSchnorrSignature {
813
+ /** Schnorr scalar `s` as `u256`. */
657
814
  signature: bigint;
815
+ /** Ethereum-style 20-byte address of the nonce point `R`, as a `felt252`. */
658
816
  commitment: bigint;
817
+ /** Big-endian signers bitmap as `u256`; bit `i` is node `i` of the registry version. */
659
818
  signers_bitmap: bigint;
660
819
  }
661
- interface StarknetVerifierArgs {
662
- dataUpdate: StarknetDataUpdate;
820
+ /** Starknet calldata shape for `Attestation`. */
821
+ interface StarknetAttestation {
822
+ payload: StarknetAttestationPayload;
663
823
  signature: StarknetSchnorrSignature;
664
824
  }
665
- /** Convert a 20-byte EVM-style address hex to a Starknet felt-compatible bigint. */
825
+ /** Positional arguments for `verify(attestation, max_age)`. */
826
+ interface StarknetVerifierArgs {
827
+ attestation: StarknetAttestation;
828
+ /** Freshness window in seconds (`u64`). `0` disables the check. */
829
+ maxAge: number;
830
+ }
831
+ interface BuildStarknetVerifierArgsOptions {
832
+ /**
833
+ * Freshness window in seconds. The verifier rejects the attestation with `STALE` when it
834
+ * is older than this, and as `MALFORMED` when it is dated in the future.
835
+ *
836
+ * Required on purpose. `0` disables the check entirely, and that is not a neutral
837
+ * default: the verifier is stateless, so without a window it accepts a correctly signed
838
+ * attestation forever. Pass `0` only when the consuming contract enforces freshness or
839
+ * ordering itself.
840
+ */
841
+ maxAge: number;
842
+ }
843
+ /** Decoded `verify` return value. */
844
+ interface StarknetVerifyResult {
845
+ success: boolean;
846
+ /** Raw result code — see `VERIFY_CODES`. */
847
+ code: number;
848
+ /** Name of `code`, or `"UNKNOWN"` for a code newer than this SDK. */
849
+ reason: VerifyCodeName | "UNKNOWN";
850
+ }
851
+ /** A felt as returned by `starknet_call` (hex or decimal string) or by an ABI-aware client. */
852
+ type StarknetFeltLike = string | number | bigint | boolean;
853
+ /** Convert a 20-byte commitment address hex to a Starknet felt-compatible bigint. */
666
854
  declare function commitmentAddressToStarknetFelt(value: string): bigint;
667
855
  /** Convert a 32-byte bitmap hex to a Starknet/Cairo `u256` bigint. */
668
856
  declare function signersBitmapToStarknetUint256(value: string): bigint;
669
- /** Build verifier `DataUpdate` and `SchnorrSignature` structs from a gateway result. */
670
- declare function buildStarknetVerifierArgs(result: DataUpdateResult): StarknetVerifierArgs;
857
+ /**
858
+ * Build the `verify(attestation, max_age)` arguments from a gateway result.
859
+ *
860
+ * ```ts
861
+ * const { attestation, maxAge } = buildStarknetVerifierArgs(result, { maxAge: 300 });
862
+ * ```
863
+ */
864
+ declare function buildStarknetVerifierArgs(result: DataUpdateResult, options: BuildStarknetVerifierArgsOptions): StarknetVerifierArgs;
865
+ /**
866
+ * Flat felt calldata for `verify(attestation, max_age)`, in Cairo `Serde` order — 13 felts:
867
+ *
868
+ * `value.low, value.high, source_id.low, source_id.high, registry_version,
869
+ * signatures_required, canonical_timestamp, signature.low, signature.high, commitment,
870
+ * signers_bitmap.low, signers_bitmap.high, max_age`
871
+ *
872
+ * For a raw `starknet_call` with `entry_point_selector = selector("verify")`.
873
+ */
874
+ declare function encodeStarknetVerifyCalldata(args: StarknetVerifierArgs): `0x${string}`[];
875
+ /**
876
+ * Decode the `(bool, u8)` returned by `verify`.
877
+ *
878
+ * Accepts the raw two-felt array from `starknet_call` (e.g. `["0x1", "0x0"]`) or the tuple
879
+ * an ABI-aware client decodes (e.g. `{ 0: true, 1: 0n }`). Throws when the two halves
880
+ * disagree (`success` with a non-zero code, or failure with `OK`), which means the call did
881
+ * not reach a Molpha verifier of this interface.
882
+ */
883
+ declare function parseStarknetVerifyResult(result: readonly StarknetFeltLike[] | {
884
+ readonly 0: StarknetFeltLike;
885
+ readonly 1: StarknetFeltLike;
886
+ }): StarknetVerifyResult;
671
887
 
672
888
  /**
673
889
  * Consumer PDA derivations. Seed byte strings are cross-checked against the IDL const
@@ -748,4 +964,4 @@ declare class MolphaSDK {
748
964
  }>;
749
965
  }
750
966
 
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 };
967
+ export { APIConfig, AssetDomain, type AttestationMessageFields, type BuildStarknetVerifierArgsOptions, 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 StarknetAttestation, type StarknetAttestationPayload, type StarknetFeltLike, type StarknetSchnorrSignature, type StarknetVerifierArgs, type StarknetVerifyResult, type SubmitAttestationArgs, type SubmitResult, type SubscribeResult, type SubscriptionInfo, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, type TransferAuthorization, UpstreamPaymentRequiredError, UpstreamQuote, UpstreamTerms, VERIFY_CODES, type VerifyCode, type VerifyCodeName, 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, encodeStarknetVerifyCalldata, ensureLength, evmAddressFromPrivateKey, feedPda, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseGatewayInfo, parseStarknetVerifyResult, 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, verifyCodeName };