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

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
@@ -45,12 +45,14 @@ Every signed attestation commits to the same message across chains:
45
45
 
46
46
  ```text
47
47
  message = keccak256(
48
- keccak256("MOLPHA_MESSAGE_V1") || sourceId || u32be(registryVersion) ||
49
- u32be(signaturesRequired) || signersBitmap || value || u64be(canonicalTimestamp)
48
+ keccak256("MOLPHA_MESSAGE_V1") || value || sourceId || u32be(registryVersion) ||
49
+ u8(signaturesRequired) || u64be(canonicalTimestamp) || signersBitmap
50
50
  )
51
51
  ```
52
52
 
53
- `attestationMessageHash` / `attestationMessageHashFromResult` recompute it client-side.
53
+ The preimage is 141 bytes (32 + 32 + 32 + 4 + 1 + 8 + 32).
54
+
55
+ `attestationMessageHash` / `attestationMessageHashFromAttestation` recompute it client-side.
54
56
 
55
57
  ## Install
56
58
 
@@ -60,15 +62,6 @@ pnpm add @molpha/sdk
60
62
 
61
63
  Runtime dependencies include `@solana/kit`, `@anchor-lang/core`, and `@noble/*`. `bn.js` is an optional peer dependency (used by the Solana / Anchor path).
62
64
 
63
- > **Migration from `@molpha-oracle/sdk`:** The package was renamed to `@molpha/sdk` starting at `0.1.0`. `@molpha-oracle/sdk` is deprecated — update install commands and imports:
64
- >
65
- > ```bash
66
- > pnpm remove @molpha-oracle/sdk
67
- > pnpm add @molpha/sdk
68
- > ```
69
- >
70
- > Replace `@molpha-oracle/sdk` with `@molpha/sdk` in all import paths (including `@molpha/sdk/utils`).
71
-
72
65
  | Import | Use |
73
66
  |---|---|
74
67
  | `@molpha/sdk` | Facade (`MolphaSDK`), `MolphaGateway`, `MolphaSolanaClient`, core hashing (`deriveSourceId`, `attestationMessageHash`, `hashRequestAuth`), EVM/Starknet helpers. Browser-safe; no `fs` in the main entry. |
@@ -108,7 +101,7 @@ const { result, signature, feed } = await sdk.requestAndSubmit({
108
101
  });
109
102
 
110
103
  // The round's identity, for reads and cross-chain verification.
111
- const sourceId = deriveSourceIdString(apiConfig); // === result.sourceId
104
+ const sourceId = deriveSourceIdString(apiConfig); // === result.payload.sourceId
112
105
  ```
113
106
 
114
107
  `requestAndSubmit` requests a threshold-signed attestation from the gateway (against the current on-chain registry version) and submits it to Solana via `submit_attestation` in one call. The first successful submit creates the feed account for `(sourceId, signaturesRequired, submitter)` if it does not already exist.
@@ -223,8 +216,7 @@ import { web3 } from "@anchor-lang/core";
223
216
  import {
224
217
  MolphaSDK,
225
218
  PlanType,
226
- deriveApiConfigHash,
227
- deriveFeedIdString,
219
+ deriveSourceIdString,
228
220
  } from "@molpha/sdk";
229
221
  import { walletFromKeypairFile } from "@molpha/sdk/utils";
230
222
 
@@ -268,6 +260,8 @@ const apiConfig = {
268
260
  const sourceId = deriveSourceIdString(apiConfig); // 64 hex chars, no 0x
269
261
  ```
270
262
 
263
+ The canonical JSON has the fixed key order `url`, `method`, `headers` (sorted by UTF-16 code units, `{}` if empty), `responseParser`, `valueTransform` and, for [tolerance mode](#median-tolerance-mode), a trailing `aggregation`. The key is omitted for exact mode, so existing five-field configs keep their `sourceId`.
264
+
271
265
  The gateway, the Solana program and the EVM/Starknet verifiers all recompute `sourceId` from the same canonical config, so pass the same `apiConfig` (including `{{secret.*}}` placeholders) every time. `signaturesRequired` is **not** part of `sourceId`: on Solana a feed account is keyed by `(sourceId, signaturesRequired, submitter)`, so the same source can be tracked at different quorums.
272
266
 
273
267
  ### 3. Request signed data from the gateway
@@ -282,7 +276,9 @@ const result = await sdk.gateway.requestSignedData({
282
276
 
283
277
  The gateway round uses the current on-chain registry version. Selected verifier nodes independently fetch/recompute the result and sign only if the observed value matches the canonical result.
284
278
 
285
- The returned `DataUpdateResult` includes `sourceId`, the signed value, canonical timestamp, registry version, required quorum, signer bitmap, and aggregate signature. `signaturesRequired` must be at least the protocol's `min_signers` (currently 3) or the chain rejects the submit.
279
+ The returned `Attestation` matches the cross-VM struct (`payload` + `signature`), plus gateway-only `value` (human-readable) and `fresh`. `payload` carries `sourceId`, the signed 32-byte `value`, `canonicalTimestamp`, `registryVersion`, and `signaturesRequired`; `signature` carries the aggregate Schnorr material and `signersBitmap`. `signaturesRequired` must be at least the protocol's `min_signers` (currently 3) or the chain rejects the submit.
280
+
281
+ A gateway response is a coordination result, not proof of settlement or of on-chain verification. Consumers still decide freshness, source, quorum and replay policy.
286
282
 
287
283
  ### 4. Submit on Solana
288
284
 
@@ -290,12 +286,24 @@ The returned `DataUpdateResult` includes `sourceId`, the signed value, canonical
290
286
  const { signature, feed } = await sdk.solana.submitAttestation(result);
291
287
  ```
292
288
 
289
+ `submit_attestation` takes `{ attestation: { payload, signature }, rawValue, coalitionKey }` plus one read-only `Node` account per signer (ascending signer-bit order, resolved from the registry snapshot the round was signed against). The SDK builds all of it: it fetches the signer `Node` accounts in one batched read, sums their secp256k1 keys into the affine **coalition key** (`computeCoalitionKey`), and checks the program's signer-count bounds before sending. The coalition key is unsigned instruction data that the program checks projectively against its own sum, so a wrong key only fails the transaction. Pass `{ coalitionKey }` to skip the `Node` fetch when you already hold it.
290
+
291
+ If the signed 32-byte value is the keccak digest of a longer preimage, pass `{ rawValue }` (up to 256 bytes). The SDK verifies the digest before sending:
292
+
293
+ ```ts
294
+ const { signature, feed } = await sdk.solana.submitAttestation(result, { rawValue });
295
+ ```
296
+
293
297
  Then read the feed this wallet wrote for that source and quorum:
294
298
 
295
299
  ```ts
296
- const feedState = await sdk.solana.readFeed(result.sourceId, signaturesRequired);
300
+ const feedState = await sdk.solana.readFeed(result.payload.sourceId, signaturesRequired);
297
301
  ```
298
302
 
303
+ `FeedAccount.value` is the 32 signed bytes (`valueKind.value`) or their keccak preimage hash (`valueKind.hash`); `submitter` is the wallet that created the feed.
304
+
305
+ The subscription read no longer exposes usage. The program dropped `used_rounds` / `prepaid_usdc` / `price` from `Subscription`; round quota is counted by the gateway's off-chain outbox, so `SubscriptionInfo` carries only `owner`, `planType`, `validUntil`, `maxRounds`, `delegateCount`, `maxDelegates`, `maxSigners`.
306
+
299
307
  ### One-call request + submit
300
308
 
301
309
  ```ts
@@ -345,6 +353,54 @@ refresh the context when the on-chain registry changes. The SDK derives the
345
353
  selection from the on-chain `nodeCount` and refuses a cached node list whose length
346
354
  disagrees with it. The same `context` field is accepted by `requestAndSubmit`.
347
355
 
356
+ ## Median tolerance mode
357
+
358
+ By default every signing node must observe the identical value. For noisy numeric sources
359
+ (prices), add an `aggregation` object and the selected nodes instead exchange signed
360
+ observations, drop stale ones and outliers, and sign the **lower median** of
361
+ `signaturesRequired` surviving observations:
362
+
363
+ ```ts
364
+ const apiConfig = {
365
+ url: "https://api.example.com/price",
366
+ responseParser: "$.price",
367
+ aggregation: {
368
+ mode: "tolerance",
369
+ rule: "median",
370
+ maxDeviationBps: 50, // u32: allowed distance from the lower median, in bps
371
+ maxAgeMs: 2000, // > 0: max observation age in milliseconds
372
+ numeric: { type: "int256", decimals: 8 }, // value = round-half-even(price * 10^8)
373
+ },
374
+ };
375
+
376
+ const { result } = await sdk.requestAndSubmit({ apiConfig, signaturesRequired: 5 });
377
+ ```
378
+
379
+ Rules (the node is the reference; the SDK rejects violations before any request is made, with `AggregationConfigError`):
380
+
381
+ - `aggregation` is part of the `sourceId` and is **omitted** for exact mode. `mode: "exact"` is rejected because it would change the identity.
382
+ - Only `rule: "median"` and `numeric.type: "int256"` are supported. `decimals` is `0..255`, `maxDeviationBps` a u32, `maxAgeMs` a positive integer.
383
+ - Tolerance needs `signaturesRequired >= 3`.
384
+ - The nested object is rebuilt in canonical order (`mode`, `rule`, `maxDeviationBps`, `maxAgeMs`, `numeric{type, decimals}`); extra or reordered keys on your input never reach the hash. For the config above, `sourceId` hashes exactly `{"url":...,"method":"GET","headers":{},"responseParser":"$.price","valueTransform":"","aggregation":{"mode":"tolerance","rule":"median","maxDeviationBps":50,"maxAgeMs":2000,"numeric":{"type":"int256","decimals":8}}}` (pinned against the node's Go test `TestAggregationSourceIdentity`).
385
+ - The signed value is a signed `int256` (two's-complement `bytes32`). For tolerance results `Attestation.value` is rendered from the signed `payload.value` at the source's `decimals`, and `signature.signersBitmap` / signature fields must come from the response, since the nodes choose the final signing set after the observation exchange.
386
+ - The gateway must forward `aggregation` to the nodes. If it derives a different identity, the response's `sourceId` / `configHash` will not match and the SDK throws; a gateway that echoes `aggregation` in its response must echo the requested policy.
387
+
388
+ Helpers for the signed value (mirror the node's `new/tolerance` package):
389
+
390
+ ```ts
391
+ import {
392
+ encodeInt256Decimal, // ("42150.12345678", 8) -> 32-byte Uint8Array, round half to even, bounds-checked
393
+ decodeInt256, // bytes32 (Uint8Array | hex) -> bigint
394
+ formatInt256Decimal, // bytes32 or bigint, decimals -> "42150.12345678" (trailing zeros trimmed)
395
+ encodeInt256, // bigint -> bytes32
396
+ INT256_MIN,
397
+ INT256_MAX,
398
+ } from "@molpha/sdk";
399
+
400
+ const feedState = await sdk.solana.readFeed(result.payload.sourceId, 5);
401
+ const price = formatInt256Decimal(Uint8Array.from(feedState!.value), 8);
402
+ ```
403
+
348
404
  ## Private APIs and encrypted secrets
349
405
 
350
406
  Sources can use private APIs without sending plaintext secrets to the gateway.
@@ -366,7 +422,7 @@ const result = await sdk.gateway.requestSignedData({
366
422
 
367
423
  `MolphaSDK` wires `verifyNodeKeys` to `solana.verifyNodeKeysForPrivateApi`, which authenticates gateway node encryption keys against the on-chain `Node` accounts of the round's registry snapshot (`registry.nodes[index]`) before secrets are encrypted.
368
424
 
369
- Secrets are encrypted into per-node envelopes. The gateway coordinates the round but should not receive plaintext API credentials.
425
+ Secrets are encrypted into per-node envelopes. The gateway coordinates the round but should not receive plaintext API credentials. The encrypted plaintext is the canonical config (including `aggregation`) with secrets substituted.
370
426
 
371
427
  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
428
 
@@ -455,90 +511,121 @@ Supported network ids (selection helpers only): `evm-sepolia`, `arbitrum-sepolia
455
511
 
456
512
  ### Build verifier arguments
457
513
 
514
+ The verifier's entrypoint is
515
+ `verify(Attestation attestation, uint64 maxAge) returns (bool success, uint8 code)`.
516
+
458
517
  ```ts
459
518
  import { buildEvmVerifierArgs } from "@molpha/sdk";
460
519
 
461
520
  const result = await sdk.gateway.requestSignedData({ apiConfig, signaturesRequired });
462
521
 
463
- const { dataUpdate, signature } = buildEvmVerifierArgs(result);
522
+ const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
464
523
  ```
465
524
 
466
- The generated tuples match the Molpha EVM verifier ABI:
525
+ `maxAge` is required. It is the freshness window in seconds: the verifier reports an older
526
+ attestation as `STALE`, and one dated after `block.timestamp` as `MALFORMED`. `0` disables the
527
+ check entirely — pass it only when your contract enforces freshness or ordering itself, because
528
+ a stateless verifier otherwise accepts a correctly signed attestation forever.
467
529
 
468
- ```ts
469
- // dataUpdate:
470
- // [bytes32 sourceId,
471
- // uint32 registryVersion,
472
- // uint32 signaturesRequired,
473
- // bytes32 valuePacked,
474
- // uint64 timestamp]
530
+ The generated object matches the Solidity `IVerifier.Attestation` struct, using viem's
531
+ primitive types so it passes straight into `readContract` or an ethers `Contract`. Member
532
+ order is ABI order (and the signed message's order). The gateway `Attestation` uses the same
533
+ nested shape with lowercase hex strings instead of viem primitives:
475
534
 
476
- // signature:
477
- // [bytes32 s,
478
- // address commitment,
479
- // uint256 signersBitmap]
535
+ ```ts
536
+ attestation:
537
+ {
538
+ payload: {
539
+ value: `0x${string}`, // bytes32 (Attestation.payload.value)
540
+ sourceId: `0x${string}`, // bytes32
541
+ registryVersion: number, // uint32
542
+ signaturesRequired: number, // uint8
543
+ canonicalTimestamp: bigint, // uint64
544
+ },
545
+ signature: {
546
+ signature: `0x${string}`, // bytes32 (Attestation.signature.s)
547
+ commitment: `0x${string}`, // address (Attestation.signature.commitmentAddr)
548
+ signersBitmap: bigint, // uint256
549
+ },
550
+ }
551
+ maxAge: bigint // uint64
480
552
  ```
481
553
 
482
- Note: the deployed contract's source names the first struct field `jobId`; the SDK ABI names it `sourceId` (component names do not affect encoding) and the value is the 32-byte source id.
554
+ The builder range-checks every integer against its Solidity type. Out-of-range calldata never
555
+ reaches `verify`: the ABI decoder reverts on it instead of returning a result code.
483
556
 
484
- ### ethers
557
+ ### viem
485
558
 
486
559
  ```ts
487
- import { Contract } from "ethers";
560
+ import { createPublicClient, http } from "viem";
488
561
  import {
489
562
  buildEvmVerifierArgs,
563
+ MOLPHA_VERIFIER_ABI,
490
564
  MOLPHA_VERIFIER_ADDRESS,
565
+ parseEvmVerifyResult,
491
566
  } from "@molpha/sdk";
492
567
 
493
- const verifier = new Contract(
494
- MOLPHA_VERIFIER_ADDRESS,
495
- abi,
496
- signer,
497
- );
568
+ const client = createPublicClient({ chain, transport: http() });
569
+ const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
498
570
 
499
- const { dataUpdate, signature } = buildEvmVerifierArgs(result);
571
+ const returned = await client.readContract({
572
+ address: MOLPHA_VERIFIER_ADDRESS,
573
+ abi: MOLPHA_VERIFIER_ABI,
574
+ functionName: "verify",
575
+ args: [attestation, maxAge],
576
+ });
500
577
 
501
- await verifier.verify(dataUpdate, signature);
578
+ const { success, code, reason } = parseEvmVerifyResult(returned);
579
+ // { success: true, code: 0, reason: "OK" }
580
+ // { success: false, code: 10, reason: "STALE" }
502
581
  ```
503
582
 
504
- ### viem
583
+ ### ethers
505
584
 
506
585
  ```ts
507
- import { createPublicClient, http } from "viem";
586
+ import { Contract } from "ethers";
508
587
  import {
509
588
  buildEvmVerifierArgs,
510
589
  MOLPHA_VERIFIER_ABI,
511
590
  MOLPHA_VERIFIER_ADDRESS,
591
+ parseEvmVerifyResult,
512
592
  } from "@molpha/sdk";
513
593
 
514
- const client = createPublicClient({
515
- chain,
516
- transport: http(),
517
- });
594
+ const verifier = new Contract(MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_ABI, provider);
595
+ const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
518
596
 
519
- const { dataUpdate, signature } = buildEvmVerifierArgs(result);
597
+ const { success, code, reason } = parseEvmVerifyResult(await verifier.verify(attestation, maxAge));
598
+ ```
520
599
 
521
- await client.readContract({
522
- address: MOLPHA_VERIFIER_ADDRESS,
523
- abi: MOLPHA_VERIFIER_ABI,
524
- functionName: "verify",
525
- args: [
526
- {
527
- sourceId: dataUpdate[0],
528
- registryVersion: dataUpdate[1],
529
- signaturesRequired: dataUpdate[2],
530
- value: dataUpdate[3],
531
- canonicalTimestamp: BigInt(dataUpdate[4]),
532
- },
533
- {
534
- signature: signature[0],
535
- commitment: signature[1],
536
- signersBitmap: signature[2],
537
- },
538
- ],
539
- });
600
+ ### Raw `eth_call`
601
+
602
+ `encodeEvmVerifyCalldata` produces the full calldata (selector `0x67e2907b` plus nine static
603
+ words), and `parseEvmVerifyResult` also accepts the raw 64-byte return data:
604
+
605
+ ```ts
606
+ import { buildEvmVerifierArgs, encodeEvmVerifyCalldata, parseEvmVerifyResult } from "@molpha/sdk";
607
+
608
+ const args = buildEvmVerifierArgs(result, { maxAge: 300 });
609
+ const returnData = await provider.call({ to: verifierAddress, data: encodeEvmVerifyCalldata(args) });
610
+
611
+ const { success, code, reason } = parseEvmVerifyResult(returnData);
540
612
  ```
541
613
 
614
+ `verify` never reverts; a rejection is a result code from the shared `VERIFY_CODES` table (see
615
+ [the Starknet section](#call-verify-and-read-the-result) for the full list). The EVM verifier
616
+ returns `MALFORMED` for a zero `signaturesRequired`, a zero or out-of-range signature scalar, a
617
+ zero commitment, or fewer set bitmap bits than `signaturesRequired`. `parseEvmVerifyResult`
618
+ throws when `success` and `code` disagree, which means the call did not reach a Molpha verifier
619
+ of this interface.
620
+
621
+ ### Registry reads
622
+
623
+ `MOLPHA_VERIFIER_ABI` also covers every read-only registry view — `getRegistryVersion`,
624
+ `getTotalNodes`, `redundancyBuffer`, `getRegistryRoot` / `getRegistryPointer` (current or per
625
+ version), `activatesAt`, `retiredAt`, `isLatestVersion`, `nodeStatus`, `isNode` — and the
626
+ `InvalidRegistryVersion` error the per-version views revert with. Owner-only mutators are not
627
+ included.
628
+
542
629
  Lower-level helpers are also exported for manual integrations:
543
630
 
544
631
  ```ts
@@ -594,7 +681,8 @@ enforces freshness or ordering itself, because a stateless verifier otherwise ac
594
681
  correctly signed attestation forever.
595
682
 
596
683
  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`:
684
+ order (and the signed message's order). The gateway `Attestation` uses the same nested shape
685
+ with lowercase hex strings instead of Cairo felts:
598
686
 
599
687
  ```ts
600
688
  attestation:
@@ -685,7 +773,9 @@ A Molpha attestation is valid only if the verifier can confirm:
685
773
  - the signer bitmap is a subset of the deterministic selection for `(sourceId, registryVersion, canonicalTimestamp)`;
686
774
  - the aggregate Schnorr signature over `attestationMessageHash(...)` is valid;
687
775
  - the timestamp is within the accepted freshness bounds;
688
- - on Solana, the signer `Node` accounts passed as remaining accounts are exactly `registry.nodes[bit]` for every set bit.
776
+ - on Solana, the signer `Node` accounts passed as remaining accounts are exactly `registry.nodes[bit]` for every set bit, and the supplied coalition key matches the sum of their keys.
777
+
778
+ A valid signature does not replace consumer policy: freshness, source, quorum and replay checks remain the consumer's responsibility.
689
779
 
690
780
  Solana verification finalizes feed state via `submit_attestation`. EVM and Starknet verification are stateless and return whether the signed Molpha attestation is valid for the deployed verifier registry.
691
781
 
@@ -701,6 +791,8 @@ import { MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS } from "@molpha/sdk";
701
791
 
702
792
  Override `idl` and `programId` when targeting another deployment.
703
793
 
794
+ The vendored IDL is `anchor idl build -p molpha` output for `molpha-solana-program` `3d01170` ("Epoch settlements (#47)").
795
+
704
796
  Keep the vendored IDL aligned with the deployed program. Mismatched IDL/program versions can produce invalid account derivations, decoding errors, or failed instruction simulation.
705
797
 
706
798
  ## Standalone clients
@@ -746,7 +838,7 @@ Current scope:
746
838
  - Solana attestation submission and feed/registry reads;
747
839
  - private API encryption helpers (pre-production);
748
840
  - 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;
841
+ - EVM and Starknet verifier argument building, `verify` calldata encoding and result decoding;
750
842
  - deployed testnet verifier address helpers.
751
843
 
752
844
  Known limitations:
@@ -760,27 +852,6 @@ Known limitations:
760
852
 
761
853
  Solana paths such as selection bitmap and `submit_attestation` remaining-accounts resolution are aligned with the Molpha program version vendored in this repo (`MoLFnEbuMS5gWnXNfUMLAYSqRM3eQZKWRzjeMQfqbT3`, not yet deployed).
762
854
 
763
- ## Migrating from 0.1.x
764
-
765
- | Before | After |
766
- |---|---|
767
- | `deriveFeedId(owner, apiConfigHash, sigReq)` / `deriveFeedIdString` | removed — use `deriveSourceId(apiConfig)` / `deriveSourceIdString` |
768
- | `deriveApiConfigHash(apiConfig)` | `deriveSourceId(apiConfig)` (old name kept as a deprecated alias, same bytes) |
769
- | `requestSignedData({ feedId, ... })` | `requestSignedData({ apiConfig, signaturesRequired, ... })` — `sourceId` is derived from `apiConfig` |
770
- | `prepareContext(feedId)` | `prepareContext()` |
771
- | `requestAndSubmit(feedId, opts)` | `requestAndSubmit(opts)` |
772
- | `authMessage(feedId, timestamp)` (sha256) | `hashRequestAuth({ programId, gateway, sourceId, signaturesRequired, timestamp })` (keccak) |
773
- | `endpoints: string[]` | `endpoints: (string \| { url, gatewayAuthority })[]` |
774
- | `submitDataUpdate(result)` | `submitAttestation(result)` (deprecated alias kept); returns `{ signature, feed }` |
775
- | `readFeed(feedId)` | `readFeed(sourceId, signaturesRequired, submitter?)` |
776
- | `result.feedId` / `NodeKeyVerifierArgs.feedId` | `.sourceId` |
777
- | EVM tuple `feedId`, ABI `jobId` | `sourceId` |
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` |
782
- | `resolveRegistryIndexForVersion`, `VIRTUAL_INDEX`, `nodePda(index)` | removed — signer accounts are `registry.nodes[bit]`; `nodePda(owner)` |
783
-
784
855
  ## Develop
785
856
 
786
857
  ```bash
@@ -861,12 +932,6 @@ Versions follow semver and are driven by the nature of each change, not by the b
861
932
 
862
933
  The `dev` workflow should guard against this and fail until a stable `latest` exists.
863
934
 
864
- 3. Deprecate the legacy package name on npm (one-time, after `@molpha/sdk@0.1.0` is published):
865
-
866
- ```bash
867
- npm deprecate "@molpha-oracle/sdk" "Package renamed to @molpha/sdk. Please migrate."
868
- ```
869
-
870
935
  If `latest` ever points to a prerelease, repoint it after publishing a stable version:
871
936
 
872
937
  ```bash
@@ -1,6 +1,5 @@
1
1
  import { web3 } from '@anchor-lang/core';
2
2
  import { address, getAddressEncoder, getAddressDecoder } from '@solana/kit';
3
- import { ed25519 } from '@noble/curves/ed25519.js';
4
3
 
5
4
  // src/solana/kit.ts
6
5
  var TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
@@ -38,18 +37,7 @@ function setComputeUnitLimit(units) {
38
37
  function keypairFromSecretKey(secretKey) {
39
38
  return web3.Keypair.fromSecretKey(secretKey);
40
39
  }
41
- var isKeypair = (value) => typeof value === "object" && value !== null && "secretKey" in value && value.secretKey instanceof Uint8Array;
42
- function signerFromKeypair(keypair) {
43
- const seed = keypair.secretKey.slice(0, 32);
44
- return async (message) => ed25519.sign(message, seed);
45
- }
46
- function gatewaySignerFromWallet(wallet) {
47
- const molpha = wallet;
48
- if (molpha.signAuthMessage) return molpha.signAuthMessage;
49
- if ("payer" in wallet && isKeypair(wallet.payer)) return signerFromKeypair(wallet.payer);
50
- return void 0;
51
- }
52
40
 
53
- export { SYSTEM_PROGRAM_ADDRESS, TOKEN_PROGRAM_ADDRESS, addressBytes, addressFromBytes, findProgramAddressSync, gatewaySignerFromWallet, getAssociatedTokenAddressSync, keypairFromSecretKey, setComputeUnitLimit, signerFromKeypair, toPublicKey, toSolanaAddress };
54
- //# sourceMappingURL=chunk-DT6IKCWB.js.map
55
- //# sourceMappingURL=chunk-DT6IKCWB.js.map
41
+ export { SYSTEM_PROGRAM_ADDRESS, TOKEN_PROGRAM_ADDRESS, addressBytes, addressFromBytes, findProgramAddressSync, getAssociatedTokenAddressSync, keypairFromSecretKey, setComputeUnitLimit, toPublicKey, toSolanaAddress };
42
+ //# sourceMappingURL=chunk-2X22JY7Q.js.map
43
+ //# sourceMappingURL=chunk-2X22JY7Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/solana/kit.ts"],"names":[],"mappings":";;;;AAaO,IAAM,qBAAA,GAAwB,QAAQ,6CAA6C;AACnF,IAAM,gCAAA,GAAmC,OAAA;AAAA,EAC9C;AACF,CAAA;AACO,IAAM,sBAAA,GAAyB,QAAQ,kCAAkC;AAEhF,IAAM,iBAAiB,iBAAA,EAAkB;AACzC,IAAM,iBAAiB,iBAAA,EAAkB;AAElC,SAAS,gBAAgB,KAAA,EAA+B;AAC7D,EAAA,OAAO,QAAQ,OAAO,KAAA,KAAU,WAAW,KAAA,GAAQ,KAAA,CAAM,UAAU,CAAA;AACrE;AAEO,SAAS,YAAY,KAAA,EAA2D;AACrF,EAAA,OAAO,iBAAiB,IAAA,CAAK,SAAA,GAAY,QAAQ,IAAI,IAAA,CAAK,UAAU,KAAK,CAAA;AAC3E;AAEO,SAAS,aAAa,KAAA,EAAkC;AAC7D,EAAA,OAAO,WAAW,IAAA,CAAK,cAAA,CAAe,OAAO,eAAA,CAAgB,KAAK,CAAC,CAAC,CAAA;AACtE;AAGO,SAAS,iBAAiB,KAAA,EAA4B;AAC3D,EAAA,OAAO,cAAA,CAAe,OAAO,KAAK,CAAA;AACpC;AAEO,SAAS,sBAAA,CACd,OACA,cAAA,EACS;AACT,EAAA,MAAM,CAAC,GAAG,CAAA,GAAI,IAAA,CAAK,UAAU,sBAAA,CAAuB,KAAA,EAAO,WAAA,CAAY,cAAc,CAAC,CAAA;AACtF,EAAA,OAAO,gBAAgB,GAAG,CAAA;AAC5B;AAUO,SAAS,6BAAA,CACd,IAAA,EACA,KAAA,EACA,YAAA,GAA8B,qBAAA,EACrB;AACT,EAAA,OAAO,sBAAA;AAAA,IACL,CAAC,aAAa,KAAK,CAAA,EAAG,aAAa,YAAY,CAAA,EAAG,YAAA,CAAa,IAAI,CAAC,CAAA;AAAA,IACpE;AAAA,GACF;AACF;AAEO,SAAS,oBAAoB,KAAA,EAAkC;AACpE,EAAA,OAAO,IAAA,CAAK,oBAAA,CAAqB,mBAAA,CAAoB,EAAE,OAAO,CAAA;AAChE;AAEO,SAAS,qBAAqB,SAAA,EAAsC;AACzE,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,SAAS,CAAA;AAC7C","file":"chunk-2X22JY7Q.js","sourcesContent":["import { web3 } from \"@anchor-lang/core\";\nimport { address, getAddressDecoder, getAddressEncoder, type Address } from \"@solana/kit\";\n\nexport type SolanaAddress = Address | string | InstanceType<typeof web3.PublicKey>;\nexport type SolanaConnection = InstanceType<typeof web3.Connection>;\nexport type SolanaKeypair = InstanceType<typeof web3.Keypair>;\nexport type SolanaInstruction = InstanceType<typeof web3.TransactionInstruction>;\nexport type SolanaAccountMeta = {\n pubkey: InstanceType<typeof web3.PublicKey>;\n isSigner: boolean;\n isWritable: boolean;\n};\n\nexport const TOKEN_PROGRAM_ADDRESS = address(\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\");\nexport const ASSOCIATED_TOKEN_PROGRAM_ADDRESS = address(\n \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n);\nexport const SYSTEM_PROGRAM_ADDRESS = address(\"11111111111111111111111111111111\");\n\nconst addressEncoder = getAddressEncoder();\nconst addressDecoder = getAddressDecoder();\n\nexport function toSolanaAddress(value: SolanaAddress): Address {\n return address(typeof value === \"string\" ? value : value.toBase58());\n}\n\nexport function toPublicKey(value: SolanaAddress): InstanceType<typeof web3.PublicKey> {\n return value instanceof web3.PublicKey ? value : new web3.PublicKey(value);\n}\n\nexport function addressBytes(value: SolanaAddress): Uint8Array {\n return Uint8Array.from(addressEncoder.encode(toSolanaAddress(value)));\n}\n\n/** Base58 `Address` from 32 raw bytes (e.g. a `Registry.nodes[i]` entry). */\nexport function addressFromBytes(bytes: Uint8Array): Address {\n return addressDecoder.decode(bytes);\n}\n\nexport function findProgramAddressSync(\n seeds: Uint8Array[],\n programAddress: SolanaAddress,\n): Address {\n const [pda] = web3.PublicKey.findProgramAddressSync(seeds, toPublicKey(programAddress));\n return toSolanaAddress(pda);\n}\n\nexport function findProgramPublicKeySync(\n seeds: Uint8Array[],\n programAddress: SolanaAddress,\n): InstanceType<typeof web3.PublicKey> {\n const [pda] = web3.PublicKey.findProgramAddressSync(seeds, toPublicKey(programAddress));\n return pda;\n}\n\nexport function getAssociatedTokenAddressSync(\n mint: SolanaAddress,\n owner: SolanaAddress,\n tokenProgram: SolanaAddress = TOKEN_PROGRAM_ADDRESS,\n): Address {\n return findProgramAddressSync(\n [addressBytes(owner), addressBytes(tokenProgram), addressBytes(mint)],\n ASSOCIATED_TOKEN_PROGRAM_ADDRESS,\n );\n}\n\nexport function setComputeUnitLimit(units: number): SolanaInstruction {\n return web3.ComputeBudgetProgram.setComputeUnitLimit({ units });\n}\n\nexport function keypairFromSecretKey(secretKey: Uint8Array): SolanaKeypair {\n return web3.Keypair.fromSecretKey(secretKey);\n}\n\nexport function generateKeypair(): SolanaKeypair {\n return web3.Keypair.generate();\n}\n"]}