@molpha/sdk 0.2.0-dev-20260912150104 → 0.2.0-dev-20261001084453
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 +172 -68
- package/dist/index.d.ts +440 -83
- package/dist/index.js +2229 -4938
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/{wallet-B3mYmC2b.d.ts → wallet-DwdBZgl1.d.ts} +1 -1
- package/idl/molpha.json +1780 -4801
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,8 +45,8 @@ 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
|
-
|
|
48
|
+
keccak256("MOLPHA_MESSAGE_V1") || value || sourceId || u32be(registryVersion) ||
|
|
49
|
+
u8(signaturesRequired) || u64be(canonicalTimestamp) || signersBitmap
|
|
50
50
|
)
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -290,6 +290,13 @@ The returned `DataUpdateResult` includes `sourceId`, the signed value, canonical
|
|
|
290
290
|
const { signature, feed } = await sdk.solana.submitAttestation(result);
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
+
If the signed 32-byte value is the keccak digest of a longer preimage, provide the
|
|
294
|
+
preimage explicitly (up to 256 bytes). The SDK verifies the digest before sending:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
const { signature, feed } = await sdk.solana.submitAttestation(result, { rawValue });
|
|
298
|
+
```
|
|
299
|
+
|
|
293
300
|
Then read the feed this wallet wrote for that source and quorum:
|
|
294
301
|
|
|
295
302
|
```ts
|
|
@@ -455,90 +462,120 @@ Supported network ids (selection helpers only): `evm-sepolia`, `arbitrum-sepolia
|
|
|
455
462
|
|
|
456
463
|
### Build verifier arguments
|
|
457
464
|
|
|
465
|
+
The verifier's entrypoint is
|
|
466
|
+
`verify(Attestation attestation, uint64 maxAge) returns (bool success, uint8 code)`.
|
|
467
|
+
|
|
458
468
|
```ts
|
|
459
469
|
import { buildEvmVerifierArgs } from "@molpha/sdk";
|
|
460
470
|
|
|
461
471
|
const result = await sdk.gateway.requestSignedData({ apiConfig, signaturesRequired });
|
|
462
472
|
|
|
463
|
-
const {
|
|
473
|
+
const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
|
|
464
474
|
```
|
|
465
475
|
|
|
466
|
-
|
|
476
|
+
`maxAge` is required. It is the freshness window in seconds: the verifier reports an older
|
|
477
|
+
attestation as `STALE`, and one dated after `block.timestamp` as `MALFORMED`. `0` disables the
|
|
478
|
+
check entirely — pass it only when your contract enforces freshness or ordering itself, because
|
|
479
|
+
a stateless verifier otherwise accepts a correctly signed attestation forever.
|
|
467
480
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
// uint32 registryVersion,
|
|
472
|
-
// uint32 signaturesRequired,
|
|
473
|
-
// bytes32 valuePacked,
|
|
474
|
-
// uint64 timestamp]
|
|
481
|
+
The generated object matches the Solidity `IVerifier.Attestation` struct, using viem's
|
|
482
|
+
primitive types so it passes straight into `readContract` or an ethers `Contract`. Member
|
|
483
|
+
order is ABI order (and the signed message's order), so it differs from `DataUpdateResult`:
|
|
475
484
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
485
|
+
```ts
|
|
486
|
+
attestation:
|
|
487
|
+
{
|
|
488
|
+
payload: {
|
|
489
|
+
value: `0x${string}`, // bytes32 (result.valuePacked)
|
|
490
|
+
sourceId: `0x${string}`, // bytes32
|
|
491
|
+
registryVersion: number, // uint32
|
|
492
|
+
signaturesRequired: number, // uint8
|
|
493
|
+
canonicalTimestamp: bigint, // uint64 (result.timestamp)
|
|
494
|
+
},
|
|
495
|
+
signature: {
|
|
496
|
+
signature: `0x${string}`, // bytes32 (result.s)
|
|
497
|
+
commitment: `0x${string}`, // address (result.commitmentAddr)
|
|
498
|
+
signersBitmap: bigint, // uint256
|
|
499
|
+
},
|
|
500
|
+
}
|
|
501
|
+
maxAge: bigint // uint64
|
|
480
502
|
```
|
|
481
503
|
|
|
482
|
-
|
|
504
|
+
The builder range-checks every integer against its Solidity type. Out-of-range calldata never
|
|
505
|
+
reaches `verify`: the ABI decoder reverts on it instead of returning a result code.
|
|
483
506
|
|
|
484
|
-
###
|
|
507
|
+
### viem
|
|
485
508
|
|
|
486
509
|
```ts
|
|
487
|
-
import {
|
|
510
|
+
import { createPublicClient, http } from "viem";
|
|
488
511
|
import {
|
|
489
512
|
buildEvmVerifierArgs,
|
|
513
|
+
MOLPHA_VERIFIER_ABI,
|
|
490
514
|
MOLPHA_VERIFIER_ADDRESS,
|
|
515
|
+
parseEvmVerifyResult,
|
|
491
516
|
} from "@molpha/sdk";
|
|
492
517
|
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
abi,
|
|
496
|
-
signer,
|
|
497
|
-
);
|
|
518
|
+
const client = createPublicClient({ chain, transport: http() });
|
|
519
|
+
const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
|
|
498
520
|
|
|
499
|
-
const
|
|
521
|
+
const returned = await client.readContract({
|
|
522
|
+
address: MOLPHA_VERIFIER_ADDRESS,
|
|
523
|
+
abi: MOLPHA_VERIFIER_ABI,
|
|
524
|
+
functionName: "verify",
|
|
525
|
+
args: [attestation, maxAge],
|
|
526
|
+
});
|
|
500
527
|
|
|
501
|
-
|
|
528
|
+
const { success, code, reason } = parseEvmVerifyResult(returned);
|
|
529
|
+
// { success: true, code: 0, reason: "OK" }
|
|
530
|
+
// { success: false, code: 10, reason: "STALE" }
|
|
502
531
|
```
|
|
503
532
|
|
|
504
|
-
###
|
|
533
|
+
### ethers
|
|
505
534
|
|
|
506
535
|
```ts
|
|
507
|
-
import {
|
|
536
|
+
import { Contract } from "ethers";
|
|
508
537
|
import {
|
|
509
538
|
buildEvmVerifierArgs,
|
|
510
539
|
MOLPHA_VERIFIER_ABI,
|
|
511
540
|
MOLPHA_VERIFIER_ADDRESS,
|
|
541
|
+
parseEvmVerifyResult,
|
|
512
542
|
} from "@molpha/sdk";
|
|
513
543
|
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
transport: http(),
|
|
517
|
-
});
|
|
544
|
+
const verifier = new Contract(MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_ABI, provider);
|
|
545
|
+
const { attestation, maxAge } = buildEvmVerifierArgs(result, { maxAge: 300 });
|
|
518
546
|
|
|
519
|
-
const {
|
|
547
|
+
const { success, code, reason } = parseEvmVerifyResult(await verifier.verify(attestation, maxAge));
|
|
548
|
+
```
|
|
520
549
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
{
|
|
534
|
-
signature: signature[0],
|
|
535
|
-
commitment: signature[1],
|
|
536
|
-
signersBitmap: signature[2],
|
|
537
|
-
},
|
|
538
|
-
],
|
|
539
|
-
});
|
|
550
|
+
### Raw `eth_call`
|
|
551
|
+
|
|
552
|
+
`encodeEvmVerifyCalldata` produces the full calldata (selector `0x67e2907b` plus nine static
|
|
553
|
+
words), and `parseEvmVerifyResult` also accepts the raw 64-byte return data:
|
|
554
|
+
|
|
555
|
+
```ts
|
|
556
|
+
import { buildEvmVerifierArgs, encodeEvmVerifyCalldata, parseEvmVerifyResult } from "@molpha/sdk";
|
|
557
|
+
|
|
558
|
+
const args = buildEvmVerifierArgs(result, { maxAge: 300 });
|
|
559
|
+
const returnData = await provider.call({ to: verifierAddress, data: encodeEvmVerifyCalldata(args) });
|
|
560
|
+
|
|
561
|
+
const { success, code, reason } = parseEvmVerifyResult(returnData);
|
|
540
562
|
```
|
|
541
563
|
|
|
564
|
+
`verify` never reverts; a rejection is a result code from the shared `VERIFY_CODES` table (see
|
|
565
|
+
[the Starknet section](#call-verify-and-read-the-result) for the full list). The EVM verifier
|
|
566
|
+
returns `MALFORMED` for a zero `signaturesRequired`, a zero or out-of-range signature scalar, a
|
|
567
|
+
zero commitment, or fewer set bitmap bits than `signaturesRequired`. `parseEvmVerifyResult`
|
|
568
|
+
throws when `success` and `code` disagree, which means the call did not reach a Molpha verifier
|
|
569
|
+
of this interface.
|
|
570
|
+
|
|
571
|
+
### Registry reads
|
|
572
|
+
|
|
573
|
+
`MOLPHA_VERIFIER_ABI` also covers every read-only registry view — `getRegistryVersion`,
|
|
574
|
+
`getTotalNodes`, `redundancyBuffer`, `getRegistryRoot` / `getRegistryPointer` (current or per
|
|
575
|
+
version), `activatesAt`, `retiredAt`, `isLatestVersion`, `nodeStatus`, `isNode` — and the
|
|
576
|
+
`InvalidRegistryVersion` error the per-version views revert with. Owner-only mutators are not
|
|
577
|
+
included.
|
|
578
|
+
|
|
542
579
|
Lower-level helpers are also exported for manual integrations:
|
|
543
580
|
|
|
544
581
|
```ts
|
|
@@ -578,40 +615,100 @@ const sepoliaDirect = MOLPHA_VERIFIER_STARKNET_SEPOLIA;
|
|
|
578
615
|
|
|
579
616
|
### Build verifier arguments
|
|
580
617
|
|
|
618
|
+
The verifier's entrypoint is `verify(attestation: Attestation, max_age: u64) -> (bool, u8)`.
|
|
619
|
+
|
|
581
620
|
```ts
|
|
582
621
|
import { buildStarknetVerifierArgs } from "@molpha/sdk";
|
|
583
622
|
|
|
584
623
|
const result = await sdk.gateway.requestSignedData({ apiConfig, signaturesRequired });
|
|
585
624
|
|
|
586
|
-
const {
|
|
625
|
+
const { attestation, maxAge } = buildStarknetVerifierArgs(result, { maxAge: 300 });
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
`maxAge` is required. It is the freshness window in seconds: the verifier reports an older
|
|
629
|
+
attestation as `STALE`. `0` disables the check entirely — pass it only when your contract
|
|
630
|
+
enforces freshness or ordering itself, because a stateless verifier otherwise accepts a
|
|
631
|
+
correctly signed attestation forever.
|
|
632
|
+
|
|
633
|
+
The generated object matches the Cairo `Attestation` struct. Member order is Cairo `Serde`
|
|
634
|
+
order (and the signed message's order), so it differs from `DataUpdateResult`:
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
attestation:
|
|
638
|
+
{
|
|
639
|
+
payload: {
|
|
640
|
+
value: u256,
|
|
641
|
+
source_id: u256,
|
|
642
|
+
registry_version: u32,
|
|
643
|
+
signatures_required: u8,
|
|
644
|
+
canonical_timestamp: u64,
|
|
645
|
+
},
|
|
646
|
+
signature: {
|
|
647
|
+
signature: u256,
|
|
648
|
+
commitment: felt252, // 20-byte address as felt
|
|
649
|
+
signers_bitmap: u256,
|
|
650
|
+
},
|
|
651
|
+
}
|
|
587
652
|
```
|
|
588
653
|
|
|
589
|
-
The
|
|
654
|
+
The builder range-checks every integer against its Cairo type. Out-of-range calldata never
|
|
655
|
+
reaches `verify`: Cairo `Serde` fails while decoding the arguments and the call reverts
|
|
656
|
+
instead of returning a result code.
|
|
657
|
+
|
|
658
|
+
### Call `verify` and read the result
|
|
659
|
+
|
|
660
|
+
`encodeStarknetVerifyCalldata` flattens the arguments into the 13 felts a raw `starknet_call`
|
|
661
|
+
takes, and `parseStarknetVerifyResult` decodes the `(bool, u8)` it returns. Any Starknet
|
|
662
|
+
client works; with `starknet.js`:
|
|
590
663
|
|
|
591
664
|
```ts
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
665
|
+
import { RpcProvider } from "starknet";
|
|
666
|
+
import {
|
|
667
|
+
buildStarknetVerifierArgs,
|
|
668
|
+
encodeStarknetVerifyCalldata,
|
|
669
|
+
parseStarknetVerifyResult,
|
|
670
|
+
} from "@molpha/sdk";
|
|
671
|
+
|
|
672
|
+
const provider = new RpcProvider({ nodeUrl: STARKNET_RPC_URL });
|
|
673
|
+
const args = buildStarknetVerifierArgs(result, { maxAge: 300 });
|
|
674
|
+
|
|
675
|
+
const response = await provider.callContract({
|
|
676
|
+
contractAddress: verifierAddress,
|
|
677
|
+
entrypoint: "verify",
|
|
678
|
+
calldata: encodeStarknetVerifyCalldata(args),
|
|
679
|
+
});
|
|
600
680
|
|
|
601
|
-
|
|
602
|
-
// {
|
|
603
|
-
//
|
|
604
|
-
// commitment: felt252, // EVM-style 20-byte address as felt
|
|
605
|
-
// signers_bitmap: u256,
|
|
606
|
-
// }
|
|
681
|
+
const { success, code, reason } = parseStarknetVerifyResult(response);
|
|
682
|
+
// { success: true, code: 0, reason: "OK" }
|
|
683
|
+
// { success: false, code: 10, reason: "STALE" }
|
|
607
684
|
```
|
|
608
685
|
|
|
686
|
+
`verify` never reverts on well-formed calldata; a rejection is a result code. The codes are
|
|
687
|
+
shared with the EVM verifier contract and exported as `VERIFY_CODES`:
|
|
688
|
+
|
|
689
|
+
| Code | Name | Meaning |
|
|
690
|
+
|---|---|---|
|
|
691
|
+
| 0 | `OK` | Verified |
|
|
692
|
+
| 2 | `BAD_REGISTRY_VERSION` | `registryVersion` does not exist on this verifier |
|
|
693
|
+
| 3 | `MALFORMED` | Structurally invalid input, or dated in the future when `maxAge != 0` |
|
|
694
|
+
| 4 | `NOT_YET_ACTIVE` | `canonicalTimestamp` predates the registry version's activation |
|
|
695
|
+
| 5 | `VERSION_EXPIRED` | Registry version superseded more than the grace window earlier |
|
|
696
|
+
| 7 | `BAD_QUORUM` | Signers are not within the round's derived selection group |
|
|
697
|
+
| 8 | `BAD_AGGREGATE` | The signers' aggregate key is the point at infinity |
|
|
698
|
+
| 9 | `BAD_SIGNATURE` | The aggregate Schnorr signature does not verify |
|
|
699
|
+
| 10 | `STALE` | Older than `maxAge` |
|
|
700
|
+
|
|
701
|
+
Codes 1 and 6 are reserved and never returned. Codes are append-only, so
|
|
702
|
+
`parseStarknetVerifyResult` reports a code newer than your SDK as `reason: "UNKNOWN"` rather
|
|
703
|
+
than throwing.
|
|
704
|
+
|
|
609
705
|
Lower-level helpers are also exported:
|
|
610
706
|
|
|
611
707
|
```ts
|
|
612
708
|
import {
|
|
613
709
|
commitmentAddressToStarknetFelt,
|
|
614
710
|
signersBitmapToStarknetUint256,
|
|
711
|
+
verifyCodeName,
|
|
615
712
|
} from "@molpha/sdk";
|
|
616
713
|
```
|
|
617
714
|
|
|
@@ -686,7 +783,7 @@ Current scope:
|
|
|
686
783
|
- Solana attestation submission and feed/registry reads;
|
|
687
784
|
- private API encryption helpers (pre-production);
|
|
688
785
|
- caller-funded x402 payments for paywalled API sources (Base USDC, pre-production);
|
|
689
|
-
- EVM and Starknet verifier argument building;
|
|
786
|
+
- EVM and Starknet verifier argument building, `verify` calldata encoding and result decoding;
|
|
690
787
|
- deployed testnet verifier address helpers.
|
|
691
788
|
|
|
692
789
|
Known limitations:
|
|
@@ -716,6 +813,13 @@ Solana paths such as selection bitmap and `submit_attestation` remaining-account
|
|
|
716
813
|
| `result.feedId` / `NodeKeyVerifierArgs.feedId` | `.sourceId` |
|
|
717
814
|
| EVM tuple `feedId`, ABI `jobId` | `sourceId` |
|
|
718
815
|
| Starknet `feed_id` | `source_id` |
|
|
816
|
+
| `buildStarknetVerifierArgs(result)` → `{ dataUpdate, signature }` | `buildStarknetVerifierArgs(result, { maxAge })` → `{ attestation, maxAge }` for `verify(attestation, max_age)` |
|
|
817
|
+
| `StarknetDataUpdate` (`signatures_required: u32`) | `StarknetAttestationPayload` (`signatures_required: u8`, `value` first), nested in `StarknetAttestation` |
|
|
818
|
+
| Starknet `verify` returns `bool` | returns `(bool, u8)` — decode with `parseStarknetVerifyResult` |
|
|
819
|
+
| `buildEvmVerifierArgs(result)` → `{ dataUpdate, signature }` tuples | `buildEvmVerifierArgs(result, { maxAge })` → `{ attestation, maxAge }` for `verify(attestation, maxAge)` |
|
|
820
|
+
| `EvmDataUpdateTuple` / `EvmSchnorrSignatureTuple` (positional) | `EvmAttestationPayload` / `EvmSchnorrSignature` objects (`value` first, `signaturesRequired: uint8`, `canonicalTimestamp: bigint`), nested in `EvmAttestation` |
|
|
821
|
+
| EVM `verify(DataUpdate, SchnorrSignature)` returns `bool` | `verify(Attestation, uint64)` returns `(bool, uint8)` — decode with `parseEvmVerifyResult` |
|
|
822
|
+
| `attestationMessageHash`: `sourceId ‖ u32 rv ‖ u32 sigReq ‖ bitmap ‖ value ‖ u64 ts` | `value ‖ sourceId ‖ u32 rv ‖ u8 sigReq ‖ u64 ts ‖ bitmap` (what nodes sign and every verifier checks) |
|
|
719
823
|
| `resolveRegistryIndexForVersion`, `VIRTUAL_INDEX`, `nodePda(index)` | removed — signer accounts are `registry.nodes[bit]`; `nodePda(owner)` |
|
|
720
824
|
|
|
721
825
|
## Develop
|