@molpha/sdk 0.2.0-dev-20261010103545 → 0.2.0-dev-20261011124853

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
@@ -84,7 +84,7 @@ Defaults are baked at publish time from `config/deployments.json` (`MOLPHA_SDK_P
84
84
 
85
85
  | npm dist-tag | Profile | Default gateway | Solana program | EVM / Starknet |
86
86
  |---|---|---|---|---|
87
- | `latest` | `stable` | `https://gateway.molpha.io/` | `MoLFGDpFoVnQgwbkTNScKPohCxhbfd61JjFrnotuwzh` | Documented CREATE2 / Sepolia addresses |
87
+ | `latest` | `stable` | `https://gateway.molpha.io/` | `MoLF3pgVA9MENSuEujxNNSMaTo71aLyewaH5nrWfUZs` | Documented CREATE2 / Sepolia addresses |
88
88
  | `dev` | `dev` | `https://dev-gateway.molpha.io/` | `chivcFQgxzwkpLvW41PV431HQ4dYpaW3povQH3AdpQt` | Provisional until redeploy (`MOLPHA_DEV_VERIFIERS_PROVISIONAL`); falls back to stable addresses |
89
89
 
90
90
  Inspect the active profile via `MOLPHA_SDK_PROFILE` and `MOLPHA_DEV_VERIFIERS_PROVISIONAL`. Local builds: `MOLPHA_SDK_PROFILE=stable pnpm build` or `MOLPHA_SDK_PROFILE=dev pnpm build` (default `dev`).
@@ -419,6 +419,34 @@ for (const r of results) {
419
419
 
420
420
  It reads the registry inputs once for the whole batch; bounds concurrency, starting from half the gateway's advertised `maxInflightRounds` (at most 64, else 32) and adapting (halving on a busy answer, growing back while requests succeed); runs Solana submits on their own smaller limiter (`submitConcurrency`, default 8); never discards the signed attestation of a round whose submit failed; and returns one result per feed in input order, so a failing feed does not stop the others. Pass `submit: false` to collect attestations only. Throughput is limited by the submit path (one transaction per attestation) long before it is limited by the gateway.
421
421
 
422
+ ### Batching attestations of one feed
423
+
424
+ At a high update rate, submit several attestations of the same feed in one transaction:
425
+
426
+ ```ts
427
+ const { signature, count } = await sdk.solana.submitAttestations([older, newer, newest]);
428
+ ```
429
+
430
+ Each becomes its own `submit_attestation` instruction, sorted by timestamp, and instructions run in
431
+ the order they are listed, so every attestation is applied, oldest first. Separate transactions give
432
+ no such order. Sent a few hundred milliseconds apart they reach a leader in a different order often
433
+ enough (about a third at 250 ms spacing in a devnet run) that the older one is refused with
434
+ `FeedNotNewer`; a batch makes the spacing between transactions several times longer, and one
435
+ transaction costs one fee.
436
+
437
+ - All attestations must be for one feed (the same `sourceId` and `signaturesRequired`), with distinct
438
+ timestamps. They may differ in registry version and signer set.
439
+ - The transaction is atomic. If a newer attestation of the feed has already landed, every
440
+ instruction is stale and none applies, so batch what is due together and send batches of one feed
441
+ in order.
442
+ - A transaction holds 1232 bytes, which is about three attestations with three signers, two with
443
+ four or five. `maxAttestationsPerTransaction(signerCount, { distinctSigners })` estimates it, and a
444
+ batch that does not fit throws `BatchTooLargeError` before sending, with `fits` saying how many
445
+ go: submit those and the rest apart. A priority-fee instruction costs bytes too.
446
+ - `rawValue` is not supported in a batch; use `submitAttestation` for values longer than 32 bytes.
447
+ - An attestation waits for the others in its batch, so a batch of `n` made at an interval of `t`
448
+ delays the oldest by `(n - 1) * t`.
449
+
422
450
  ### Fast requests with a cached context
423
451
 
424
452
  By default every `requestSignedData` call reads the on-chain registry up front
package/dist/index.d.ts CHANGED
@@ -12,8 +12,8 @@ import BN from 'bn.js';
12
12
  declare const MOLPHA_SDK_PROFILE: "dev";
13
13
  declare const DEFAULT_GATEWAY_ENDPOINT = "https://dev-gateway.molpha.io/";
14
14
  declare const MOLPHA_PROGRAM_ID = "chivcFQgxzwkpLvW41PV431HQ4dYpaW3povQH3AdpQt";
15
- declare const MOLPHA_VERIFIER_ADDRESS: "0xE1fd792b7E54e0C8F0Cd1c8055E446ff36d233eB";
16
- declare const MOLPHA_VERIFIER_STARKNET_SEPOLIA: "0x0378df4dbecf8f0c7daa801282932f7011c7a5e5773bab9eaf68f5fa5e7530ef";
15
+ declare const MOLPHA_VERIFIER_ADDRESS: "0xA6e49A3A1caf3E110cC04765B934C8FF118C2568";
16
+ declare const MOLPHA_VERIFIER_STARKNET_SEPOLIA: "0x032b9f47f12189668b94a5d62fb7d60f968ed0dbaf22090649d1d7a91d79cc38";
17
17
  /** True when the active profile has unset EVM/Starknet addresses (dev falls back to stable). */
18
18
  declare const MOLPHA_DEV_VERIFIERS_PROVISIONAL: true;
19
19
 
@@ -470,6 +470,33 @@ declare function planIdFromVariant(variant: Record<string, unknown>): PlanId;
470
470
  * benchmark's, and a limit that is hit fails the transaction for good.
471
471
  */
472
472
  declare function estimateSubmitComputeUnits(signerCount: number): number;
473
+ /**
474
+ * Compute-unit limit for one transaction that carries a `submit_attestation` per entry of
475
+ * `signerCounts`: the same margin as {@link estimateSubmitComputeUnits} on the sum, with the fixed
476
+ * 10k once for the transaction.
477
+ */
478
+ declare function estimateSubmitBatchComputeUnits(signerCounts: number[]): number;
479
+ /**
480
+ * Bytes of `submit_attestation` instruction data with no raw value: the 8 byte discriminator, the
481
+ * payload (32 + 32 + 4 + 1 + 8), the signature (32 + 20 + 32), the empty `Option` and the coalition
482
+ * key (32 + 32).
483
+ */
484
+ declare const SUBMIT_INSTRUCTION_DATA_BYTES = 234;
485
+ /**
486
+ * Serialized size of a transaction with `count` attestations of `signerCount` signers each, to plan
487
+ * a batch before there is one. The signer `Node` accounts are shared between attestations, so the
488
+ * size depends on how many distinct ones there are: `distinctSigners` (default `signerCount`, the
489
+ * best case; the registry's node count is the worst).
490
+ */
491
+ declare function estimateSubmitTransactionSize(count: number, signerCount: number, opts?: {
492
+ distinctSigners?: number;
493
+ priorityFee?: boolean;
494
+ }): number;
495
+ /** The most attestations of `signerCount` signers that fit in one transaction; see {@link estimateSubmitTransactionSize}. */
496
+ declare function maxAttestationsPerTransaction(signerCount: number, opts?: {
497
+ distinctSigners?: number;
498
+ priorityFee?: boolean;
499
+ }): number;
473
500
  type Commitment$1 = NonNullable<ConstructorParameters<typeof AnchorProvider>[2]>["commitment"];
474
501
  interface SubscribeResult {
475
502
  signature: string;
@@ -507,6 +534,31 @@ interface SubmitResult {
507
534
  /** Feed PDA written by this submit (`["molpha_feed", sourceId, [signaturesRequired], submitter]`). */
508
535
  feed: Address;
509
536
  }
537
+ /** What {@link MolphaSolanaClient.submitAttestations} returns. */
538
+ interface SubmitAttestationsResult extends SubmitResult {
539
+ /** Attestations in the transaction. They were applied in timestamp order. */
540
+ count: number;
541
+ }
542
+ /**
543
+ * Thrown by {@link MolphaSolanaClient.submitAttestations} when the attestations do not fit in one
544
+ * transaction. Nothing was sent: submit the first `fits` of them (oldest first) and the rest apart.
545
+ */
546
+ declare class BatchTooLargeError extends RangeError {
547
+ /** How many of the attestations, oldest first, do fit. */
548
+ readonly fits: number;
549
+ /** The serialized size of the whole batch, in bytes. */
550
+ readonly size: number;
551
+ /** The largest transaction the network accepts, in bytes. */
552
+ readonly limit: number;
553
+ readonly count: number;
554
+ constructor(
555
+ /** How many of the attestations, oldest first, do fit. */
556
+ fits: number,
557
+ /** The serialized size of the whole batch, in bytes. */
558
+ size: number,
559
+ /** The largest transaction the network accepts, in bytes. */
560
+ limit: number, count: number);
561
+ }
510
562
  /**
511
563
  * On-chain `Feed` (Anchor-decoded). `value` is always the signed
512
564
  * `AttestationPayload.value` (32 bytes): the oracle value itself when `valueKind` is
@@ -577,6 +629,8 @@ interface SubmitAttestationOptions {
577
629
  */
578
630
  rawValue?: Uint8Array;
579
631
  }
632
+ /** Options for {@link MolphaSolanaClient.submitAttestations}. */
633
+ type SubmitAttestationsOptions = Pick<SubmitAttestationOptions, "computeUnitLimit" | "priorityFeeMicroLamports">;
580
634
  interface CreateClientOpts {
581
635
  connection: SolanaConnection;
582
636
  wallet: Wallet;
@@ -659,6 +713,33 @@ declare class MolphaSolanaClient {
659
713
  * `keccak256(rawValue)`; {@link buildSubmitAttestationArgs} validates that before send.
660
714
  */
661
715
  submitAttestation(attestation: Attestation, opts?: SubmitAttestationOptions): Promise<SubmitResult>;
716
+ /**
717
+ * Submit several attestations of one feed in one transaction.
718
+ *
719
+ * Each becomes its own `submit_attestation` instruction, in timestamp order, and instructions run
720
+ * in the order they are listed: all of them are applied, oldest first, whatever order other
721
+ * transactions reach the leader in. That is what separate submits cannot promise. Transactions sent
722
+ * milliseconds apart for one feed are overtaken by a newer one often enough (about a third at 250 ms
723
+ * spacing in a devnet run) that the older one fails with `FeedNotNewer`; a batch makes the spacing
724
+ * between transactions several times longer and spends one fee and one signature for them all.
725
+ *
726
+ * The transaction is atomic: if one instruction fails (typically because a newer attestation
727
+ * of the feed landed first, so every instruction is stale) none of them applies. Batch only what
728
+ * is due together, and keep batches of one feed in the order they were made.
729
+ *
730
+ * All attestations must be for one feed (the same `sourceId` and `signaturesRequired`) with
731
+ * distinct timestamps. They need not share a registry version or a signer set. Values longer than
732
+ * 32 bytes (`rawValue`) are not supported here: submit those with {@link submitAttestation}.
733
+ *
734
+ * @throws {BatchTooLargeError} when they do not fit in one transaction (about three do with
735
+ * three signers; see {@link maxAttestationsPerTransaction}). Nothing is sent.
736
+ */
737
+ submitAttestations(attestations: Attestation[], opts?: SubmitAttestationsOptions): Promise<SubmitAttestationsResult>;
738
+ /**
739
+ * Everything one `submit_attestation` instruction needs: the arguments, the named accounts, the
740
+ * signer `Node` accounts (ascending bit order) and the feed address.
741
+ */
742
+ private prepareSubmit;
662
743
  /** The registry for `version`, reused for {@link REGISTRY_CACHE_MS}. */
663
744
  private fetchRegistryCached;
664
745
  private resolvePriorityFee;
@@ -1592,4 +1673,4 @@ declare class MolphaSDK {
1592
1673
  }>;
1593
1674
  }
1594
1675
 
1595
- export { APIConfig, AdaptiveLimiter, AggregationConfig, AggregationConfigError, AssetDomain, Attestation, type AttestationMessageFields, type BuildEvmVerifierArgsOptions, type BuildStarknetVerifierArgsOptions, type BulkFeed, type BulkOptions, type BulkResult, type CoalitionKey, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, type Eip712Domain, type EvmAttestation, type EvmAttestationPayload, type EvmSchnorrSignature, EvmSigner, type EvmVerifierArgs, type EvmVerifyResult, type FeedAccount, type GatewayEndpoint, type GatewayEndpointInput, GatewayError, type GatewayInfo, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, 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, ROUND_TICK_MS, RegistrySelectionConfig, type RegistryStateView, type RegistryView, type RequestAuthFields, type RequestSignedDataOptions, type RetryCause, type RoundContext, SELECTION_WINDOW_MS, type Secp256k1KeyInput, Signer, SourcePaymentOptions, type StarknetAttestation, type StarknetAttestationPayload, type StarknetFeltLike, type StarknetSchnorrSignature, type StarknetVerifierArgs, type StarknetVerifyResult, type SubmitAttestationArgs, type SubmitAttestationOptions, SubmitFailedError, type SubmitResult, type SubscribeResult, type SubscriptionInfo, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, type TransferAuthorization, UpstreamPaymentRequiredError, UpstreamQuote, UpstreamTerms, VERIFY_CODES, type VerifyCode, type VerifyCodeName, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitComputeUnits, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, requestMany, resolveRemainingAccounts, retryDelayMs, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, spreadOffsetMs, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
1676
+ export { APIConfig, AdaptiveLimiter, AggregationConfig, AggregationConfigError, AssetDomain, Attestation, type AttestationMessageFields, BatchTooLargeError, type BuildEvmVerifierArgsOptions, type BuildStarknetVerifierArgsOptions, type BulkFeed, type BulkOptions, type BulkResult, type CoalitionKey, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, type Eip712Domain, type EvmAttestation, type EvmAttestationPayload, type EvmSchnorrSignature, EvmSigner, type EvmVerifierArgs, type EvmVerifyResult, type FeedAccount, type GatewayEndpoint, type GatewayEndpointInput, GatewayError, type GatewayInfo, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, 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, ROUND_TICK_MS, RegistrySelectionConfig, type RegistryStateView, type RegistryView, type RequestAuthFields, type RequestSignedDataOptions, type RetryCause, type RoundContext, SELECTION_WINDOW_MS, SUBMIT_INSTRUCTION_DATA_BYTES, type Secp256k1KeyInput, Signer, SourcePaymentOptions, type StarknetAttestation, type StarknetAttestationPayload, type StarknetFeltLike, type StarknetSchnorrSignature, type StarknetVerifierArgs, type StarknetVerifyResult, type SubmitAttestationArgs, type SubmitAttestationOptions, type SubmitAttestationsOptions, type SubmitAttestationsResult, SubmitFailedError, type SubmitResult, type SubscribeResult, type SubscriptionInfo, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, type TransferAuthorization, UpstreamPaymentRequiredError, UpstreamQuote, UpstreamTerms, VERIFY_CODES, type VerifyCode, type VerifyCodeName, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitBatchComputeUnits, estimateSubmitComputeUnits, estimateSubmitTransactionSize, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, maxAttestationsPerTransaction, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, requestMany, resolveRemainingAccounts, retryDelayMs, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, spreadOffsetMs, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { toPublicKey, addressBytes, toSolanaAddress, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ADDRESS, SYSTEM_PROGRAM_ADDRESS, setComputeUnitLimit, setComputeUnitPrice, addressFromBytes, findProgramAddressSync } from './chunk-H77BBZOL.js';
1
+ import { toPublicKey, addressBytes, setComputeUnitLimit, setComputeUnitPrice, SYSTEM_PROGRAM_ADDRESS, toSolanaAddress, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ADDRESS, addressFromBytes, findProgramAddressSync } from './chunk-H77BBZOL.js';
2
2
  import { getAddressEncoder, address, getProgramDerivedAddress } from '@solana/kit';
3
3
  import { keccak_256 } from '@noble/hashes/sha3.js';
4
4
  import { utf8ToBytes, hexToBytes as hexToBytes$1, concatBytes as concatBytes$1, bytesToHex as bytesToHex$1, randomBytes } from '@noble/hashes/utils.js';
5
5
  import { secp256k1 } from '@noble/curves/secp256k1.js';
6
6
  import { gcm } from '@noble/ciphers/aes.js';
7
- import { AnchorProvider, Program, web3 } from '@anchor-lang/core';
7
+ import { web3, AnchorProvider, Program } from '@anchor-lang/core';
8
8
  import BN from 'bn.js';
9
9
  import { ed25519 } from '@noble/curves/ed25519.js';
10
10
 
@@ -205,8 +205,8 @@ function deriveSourceIdString(apiConfig) {
205
205
  var MOLPHA_SDK_PROFILE = "dev";
206
206
  var DEFAULT_GATEWAY_ENDPOINT = "https://dev-gateway.molpha.io/";
207
207
  var MOLPHA_PROGRAM_ID = "chivcFQgxzwkpLvW41PV431HQ4dYpaW3povQH3AdpQt";
208
- var MOLPHA_VERIFIER_ADDRESS = "0xE1fd792b7E54e0C8F0Cd1c8055E446ff36d233eB";
209
- var MOLPHA_VERIFIER_STARKNET_SEPOLIA = "0x0378df4dbecf8f0c7daa801282932f7011c7a5e5773bab9eaf68f5fa5e7530ef";
208
+ var MOLPHA_VERIFIER_ADDRESS = "0xA6e49A3A1caf3E110cC04765B934C8FF118C2568";
209
+ var MOLPHA_VERIFIER_STARKNET_SEPOLIA = "0x032b9f47f12189668b94a5d62fb7d60f968ed0dbaf22090649d1d7a91d79cc38";
210
210
  var MOLPHA_DEV_VERIFIERS_PROVISIONAL = true;
211
211
 
212
212
  // src/core/int256.ts
@@ -9470,11 +9470,85 @@ function estimateSubmitComputeUnits(signerCount) {
9470
9470
  const measured = 43e3 + 9200 * signerCount;
9471
9471
  return Math.min(Math.round(measured * 1.15) + 1e4, MAX_COMPUTE_UNIT_LIMIT);
9472
9472
  }
9473
+ function estimateSubmitBatchComputeUnits(signerCounts) {
9474
+ if (signerCounts.length === 0 || signerCounts.some((n) => !Number.isInteger(n) || n < 1)) {
9475
+ return MAX_COMPUTE_UNIT_LIMIT;
9476
+ }
9477
+ const measured = signerCounts.reduce((sum, n) => sum + 43e3 + 9200 * n, 0);
9478
+ return Math.min(Math.round(measured * 1.15) + 1e4, MAX_COMPUTE_UNIT_LIMIT);
9479
+ }
9480
+ var SUBMIT_INSTRUCTION_DATA_BYTES = 234;
9481
+ var PLACEHOLDER_BLOCKHASH = "11111111111111111111111111111111";
9482
+ var shortVecBytes = (n) => n < 128 ? 1 : n < 16384 ? 2 : 3;
9483
+ function transactionSize(payer, instructions) {
9484
+ const tx = new web3.Transaction();
9485
+ tx.feePayer = payer;
9486
+ tx.recentBlockhash = PLACEHOLDER_BLOCKHASH;
9487
+ tx.add(...instructions);
9488
+ const message = tx.compileMessage();
9489
+ const keys = message.accountKeys.length;
9490
+ let size = 1 + 64 + 3 + shortVecBytes(keys) + 32 * keys + 32 + shortVecBytes(message.instructions.length);
9491
+ message.instructions.forEach((compiled, i) => {
9492
+ const dataBytes = instructions[i].data.length;
9493
+ size += 1 + shortVecBytes(compiled.accounts.length) + compiled.accounts.length + shortVecBytes(dataBytes) + dataBytes;
9494
+ });
9495
+ return size;
9496
+ }
9497
+ function estimateSubmitTransactionSize(count, signerCount, opts = {}) {
9498
+ const key = () => web3.Keypair.generate().publicKey;
9499
+ const [payer, registry, feed, protocolConfig, program] = [key(), key(), key(), key(), key()];
9500
+ const distinct = Math.max(signerCount, opts.distinctSigners ?? signerCount);
9501
+ const nodes = Array.from({ length: distinct }, key);
9502
+ const instructions = [setComputeUnitLimit(1)];
9503
+ if (opts.priorityFee) instructions.push(setComputeUnitPrice(1));
9504
+ for (let i = 0; i < count; i++) {
9505
+ instructions.push(
9506
+ new web3.TransactionInstruction({
9507
+ programId: program,
9508
+ keys: [
9509
+ { pubkey: payer, isSigner: true, isWritable: true },
9510
+ { pubkey: registry, isSigner: false, isWritable: false },
9511
+ { pubkey: feed, isSigner: false, isWritable: true },
9512
+ { pubkey: protocolConfig, isSigner: false, isWritable: false },
9513
+ { pubkey: new web3.PublicKey(SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false },
9514
+ ...Array.from({ length: signerCount }, (_, j) => ({
9515
+ pubkey: nodes[(i * signerCount + j) % distinct],
9516
+ isSigner: false,
9517
+ isWritable: false
9518
+ }))
9519
+ ],
9520
+ data: Buffer.alloc(SUBMIT_INSTRUCTION_DATA_BYTES)
9521
+ })
9522
+ );
9523
+ }
9524
+ return transactionSize(payer, instructions);
9525
+ }
9526
+ function maxAttestationsPerTransaction(signerCount, opts = {}) {
9527
+ let count = 0;
9528
+ while (estimateSubmitTransactionSize(count + 1, signerCount, opts) <= web3.PACKET_DATA_SIZE) count++;
9529
+ return count;
9530
+ }
9473
9531
  var REGISTRY_CACHE_MS = 3e4;
9474
9532
  var KEY_CACHE_LIMIT = 8192;
9475
9533
  var PRIORITY_FEE_CACHE_MS = 5e3;
9476
9534
  var AUTO_FEE_PERCENTILE = 0.75;
9477
9535
  var AUTO_FEE_CAP_MICRO_LAMPORTS = 1e6;
9536
+ var BatchTooLargeError = class extends RangeError {
9537
+ constructor(fits, size, limit, count) {
9538
+ super(
9539
+ `${count} attestations need a ${size} byte transaction, over the ${limit} byte limit; ${fits} fit`
9540
+ );
9541
+ this.fits = fits;
9542
+ this.size = size;
9543
+ this.limit = limit;
9544
+ this.count = count;
9545
+ this.name = "BatchTooLargeError";
9546
+ }
9547
+ fits;
9548
+ size;
9549
+ limit;
9550
+ count;
9551
+ };
9478
9552
  var MolphaSolanaClient = class _MolphaSolanaClient {
9479
9553
  constructor(program, provider, programId) {
9480
9554
  this.program = program;
@@ -9646,6 +9720,87 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
9646
9720
  * `keccak256(rawValue)`; {@link buildSubmitAttestationArgs} validates that before send.
9647
9721
  */
9648
9722
  async submitAttestation(attestation, opts) {
9723
+ const { args, accounts, remaining, feed } = await this.prepareSubmit(attestation, opts);
9724
+ const preInstructions = [
9725
+ setComputeUnitLimit(opts?.computeUnitLimit ?? estimateSubmitComputeUnits(remaining.length))
9726
+ ];
9727
+ const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
9728
+ if (price > 0) preInstructions.push(setComputeUnitPrice(price));
9729
+ const signature = await this.methods.submitAttestation(args).accountsPartial(accounts).remainingAccounts(remaining).preInstructions(preInstructions).rpc();
9730
+ return { signature, feed };
9731
+ }
9732
+ /**
9733
+ * Submit several attestations of one feed in one transaction.
9734
+ *
9735
+ * Each becomes its own `submit_attestation` instruction, in timestamp order, and instructions run
9736
+ * in the order they are listed: all of them are applied, oldest first, whatever order other
9737
+ * transactions reach the leader in. That is what separate submits cannot promise. Transactions sent
9738
+ * milliseconds apart for one feed are overtaken by a newer one often enough (about a third at 250 ms
9739
+ * spacing in a devnet run) that the older one fails with `FeedNotNewer`; a batch makes the spacing
9740
+ * between transactions several times longer and spends one fee and one signature for them all.
9741
+ *
9742
+ * The transaction is atomic: if one instruction fails (typically because a newer attestation
9743
+ * of the feed landed first, so every instruction is stale) none of them applies. Batch only what
9744
+ * is due together, and keep batches of one feed in the order they were made.
9745
+ *
9746
+ * All attestations must be for one feed (the same `sourceId` and `signaturesRequired`) with
9747
+ * distinct timestamps. They need not share a registry version or a signer set. Values longer than
9748
+ * 32 bytes (`rawValue`) are not supported here: submit those with {@link submitAttestation}.
9749
+ *
9750
+ * @throws {BatchTooLargeError} when they do not fit in one transaction (about three do with
9751
+ * three signers; see {@link maxAttestationsPerTransaction}). Nothing is sent.
9752
+ */
9753
+ async submitAttestations(attestations, opts) {
9754
+ if (attestations.length === 0) {
9755
+ throw new RangeError("submitAttestations needs at least one attestation");
9756
+ }
9757
+ const sorted = [...attestations].sort((a, b) => a.payload.timestamp - b.payload.timestamp);
9758
+ const feedKey = (a) => `${bytesToHex(toFixedBytes(a.payload.sourceId, 32, "sourceId"))}:${a.payload.signaturesRequired}`;
9759
+ const first = feedKey(sorted[0]);
9760
+ sorted.forEach((a, i) => {
9761
+ if (feedKey(a) !== first) {
9762
+ throw new RangeError(
9763
+ "submitAttestations: every attestation must be for the same feed (sourceId and signaturesRequired)"
9764
+ );
9765
+ }
9766
+ if (i > 0 && a.payload.timestamp === sorted[i - 1].payload.timestamp) {
9767
+ throw new RangeError(
9768
+ `submitAttestations: two attestations have timestamp ${a.payload.timestamp}; the second would be refused as not newer`
9769
+ );
9770
+ }
9771
+ });
9772
+ if (sorted.length === 1) return { ...await this.submitAttestation(sorted[0], opts), count: 1 };
9773
+ const prepared = await Promise.all(sorted.map((a) => this.prepareSubmit(a)));
9774
+ const instructions = await Promise.all(
9775
+ prepared.map(
9776
+ (p) => this.methods.submitAttestation(p.args).accountsPartial(p.accounts).remainingAccounts(p.remaining).instruction()
9777
+ )
9778
+ );
9779
+ const feed = prepared[0].feed;
9780
+ const budget = [
9781
+ setComputeUnitLimit(
9782
+ opts?.computeUnitLimit ?? estimateSubmitBatchComputeUnits(prepared.map((p) => p.remaining.length))
9783
+ )
9784
+ ];
9785
+ const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
9786
+ if (price > 0) budget.push(setComputeUnitPrice(price));
9787
+ const payer = this.provider.wallet.publicKey;
9788
+ const size = transactionSize(payer, [...budget, ...instructions]);
9789
+ if (size > web3.PACKET_DATA_SIZE) {
9790
+ let fits = 0;
9791
+ while (fits < instructions.length && transactionSize(payer, [...budget, ...instructions.slice(0, fits + 1)]) <= web3.PACKET_DATA_SIZE) {
9792
+ fits++;
9793
+ }
9794
+ throw new BatchTooLargeError(fits, size, web3.PACKET_DATA_SIZE, instructions.length);
9795
+ }
9796
+ const signature = await this.provider.sendAndConfirm(new web3.Transaction().add(...budget, ...instructions));
9797
+ return { signature, feed, count: instructions.length };
9798
+ }
9799
+ /**
9800
+ * Everything one `submit_attestation` instruction needs: the arguments, the named accounts, the
9801
+ * signer `Node` accounts (ascending bit order) and the feed address.
9802
+ */
9803
+ async prepareSubmit(attestation, opts) {
9649
9804
  const { payload, signature: schnorr } = attestation;
9650
9805
  const sourceId = toFixedBytes(payload.sourceId, 32, "sourceId");
9651
9806
  const submitter = this.wallet;
@@ -9654,21 +9809,18 @@ var MolphaSolanaClient = class _MolphaSolanaClient {
9654
9809
  assertSignerCount(remaining.length, payload.signaturesRequired, registry);
9655
9810
  const coalitionKey = opts?.coalitionKey ?? await this.computeSignerCoalitionKey(remaining, registry, schnorr.signersBitmap);
9656
9811
  const feed = feedPda(sourceId, payload.signaturesRequired, submitter, this.programId);
9657
- const preInstructions = [
9658
- setComputeUnitLimit(opts?.computeUnitLimit ?? estimateSubmitComputeUnits(remaining.length))
9659
- ];
9660
- const price = await this.resolvePriorityFee(opts?.priorityFeeMicroLamports, feed);
9661
- if (price > 0) preInstructions.push(setComputeUnitPrice(price));
9662
- const signature = await this.methods.submitAttestation(
9663
- buildSubmitAttestationArgs(attestation, coalitionKey, opts?.rawValue)
9664
- ).accountsPartial({
9665
- submitter,
9666
- registry: registryPda(payload.registryVersion, this.programId),
9667
- feed,
9668
- protocolConfig: protocolConfigPda(this.programId),
9669
- systemProgram: SYSTEM_PROGRAM_ADDRESS
9670
- }).remainingAccounts(remaining).preInstructions(preInstructions).rpc();
9671
- return { signature, feed };
9812
+ return {
9813
+ args: buildSubmitAttestationArgs(attestation, coalitionKey, opts?.rawValue),
9814
+ accounts: {
9815
+ submitter,
9816
+ registry: registryPda(payload.registryVersion, this.programId),
9817
+ feed,
9818
+ protocolConfig: protocolConfigPda(this.programId),
9819
+ systemProgram: SYSTEM_PROGRAM_ADDRESS
9820
+ },
9821
+ remaining,
9822
+ feed
9823
+ };
9672
9824
  }
9673
9825
  /** The registry for `version`, reused for {@link REGISTRY_CACHE_MS}. */
9674
9826
  fetchRegistryCached(version) {
@@ -10591,6 +10743,6 @@ var MolphaSDK = class {
10591
10743
  }
10592
10744
  };
10593
10745
 
10594
- export { AdaptiveLimiter, AggregationConfigError, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, GatewayError, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, ROUND_TICK_MS, SELECTION_WINDOW_MS, SubmitFailedError, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, UpstreamPaymentRequiredError, VERIFY_CODES, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitComputeUnits, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, gatewaySignerFromWallet, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, requestMany, resolveRemainingAccounts, retryDelayMs, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signerFromKeypair, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, spreadOffsetMs, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
10746
+ export { AdaptiveLimiter, AggregationConfigError, BatchTooLargeError, DEFAULT_GATEWAY_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_ROUND_TIMEOUT_MS, EIP712_DOMAIN_TYPEHASH, GatewayError, INT256_MAX, INT256_MIN, MESSAGE_PREFIX, MIN_TOLERANCE_SIGNATURES, MOLPHA_DEV_VERIFIERS_PROVISIONAL, MOLPHA_IDL, MOLPHA_PROGRAM_ADDRESS, MOLPHA_PROGRAM_ID, MOLPHA_SDK_PROFILE, MOLPHA_VERIFIER_ABI, MOLPHA_VERIFIER_ADDRESS, MOLPHA_VERIFIER_STARKNET_ADDRESSES, MOLPHA_VERIFIER_STARKNET_SEPOLIA, MolphaGateway, MolphaSDK, MolphaSolanaClient, PlanType, REQUEST_AUTH_DOMAIN, ROUND_TICK_MS, SELECTION_WINDOW_MS, SUBMIT_INSTRUCTION_DATA_BYTES, SubmitFailedError, TRANSFER_WITH_AUTHORIZATION_TYPEHASH, UpstreamPaymentRequiredError, VERIFY_CODES, addressToBytes, assertAggregationQuorum, attestationMessageHash, attestationMessageHashFromAttestation, base64ToBytes, bigIntFromBytesBe, bitmapBitSet, bitmapToIndices, buildEvmVerifierArgs, buildStarknetVerifierArgs, buildSubmitAttestationArgs, bytesToBase64, bytesToHex, bytesToHex0x, canonicalizeAPIConfig, canonicalizeAggregation, commitmentAddressToStarknetFelt, computeCoalitionKey, concatBytes, createEvmSignerFromPrivateKey, decodeInt256, deriveGatewayPda, deriveGatewayPdaAddress, deriveGroupBitmap, deriveSelectionBitmap, deriveSelectionSeed, deriveSourceId, deriveSourceIdString, domainSeparator, effectiveSelectionSize, eligibleSetSize, encodeEvmVerifyCalldata, encodeInt256, encodeInt256Decimal, encodeRequestAuth, encodeStarknetVerifyCalldata, ensureLength, estimateSubmitBatchComputeUnits, estimateSubmitComputeUnits, estimateSubmitTransactionSize, evmAddressFromPrivateKey, feedPda, formatInt256Decimal, gatewayPda, gatewaySignerFromWallet, getMolphaStarknetVerifierAddress, hashRequestAuth, hexToBytes, maxAttestationsPerTransaction, nodePda, normalizeEndpoint, normalizeSecp256k1PublicKeyHex, parseEvmVerifyResult, parseGatewayInfo, parseStarknetVerifyResult, parseUpstreamQuote, planIdFromVariant, planPda, planVariant, probeSource, protocolConfigPda, registryPda, registryStatePda, requestMany, resolveRemainingAccounts, retryDelayMs, secp256k1PublicKeyFromCoordinates, selectedIndices, signSourcePayments, signerFromKeypair, signersBitmapToDecimal, signersBitmapToStarknetUint256, signersBitmapToUint256, spreadOffsetMs, subscriptionPda, timestampAgeSeconds, timestampSeconds, toChecksumAddress, toFixedBytes, toFixedHex, transferWithAuthorizationDigest, transferWithAuthorizationHash, u256beFromBigInt, u32be, u32le, u64be, u64le, u8, utf8, validateSuppliedTerms, verifyCodeName };
10595
10747
  //# sourceMappingURL=index.js.map
10596
10748
  //# sourceMappingURL=index.js.map