> ## Documentation Index
> Fetch the complete documentation index at: https://docs.molpha.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Read and Verify for Solana

> Consume Solana data by reading a submitted Feed account, or verify a signed payload directly with the molpha-verifier Rust crate.

There are two Solana-side consumption paths:

1. **Push then read** — submit the signed payload through `submit_data_update`, then read the maintained `Feed` account.
2. **Verify directly** — use the framework-independent [`molpha-verifier`](https://crates.io/crates/molpha-verifier) Rust crate with caller-supplied registry/node data.

Do not use an SDK simulation path as the primary Solana verification model. The on-chain program verifies when it writes, and direct verification belongs in Rust code through `molpha-verifier`.

## Read the latest result

The `Feed` account holds the latest value accepted by `submit_data_update`. The Molpha program re-verifies the aggregate signature before writing, so a valid `Feed` account is the canonical maintained state. Gateway settlement records payments in round receipts; it does not write the feed.

With the SDK:

```ts theme={null}
const feed = await sdk.solana.readFeed(feedId);
// feed.value is on-chain bytes (number[]), not the human-readable gateway string
console.log(feed?.value, feed?.canonicalTimestamp);
```

Prefer `sdk.solana.readFeed` for the maintained account. For a raw Anchor fetch without the SDK facade:

### Pattern A — off-chain (TypeScript + Anchor)

Derive the feed PDA and fetch it. If you know the owner, `apiConfigHash`, and quorum, you can compute the feed address with no on-chain lookups:

```typescript theme={null}
import { web3, AnchorProvider, Program, Wallet } from "@anchor-lang/core";
import { keccak_256 } from "@noble/hashes/sha3";
import { BN } from "bn.js";
import molphaIdl from "./molpha.json";

const PROGRAM_ID = new web3.PublicKey("MoLFGDpFoVnQgwbkTNScKPohCxhbfd61JjFrnotuwzh");
const FEED_ID_PREFIX = Buffer.from("MOLPHA_JOB_V1");
const FEED_SEED = Buffer.from("molpha_feed");

function deriveFeedId(
  owner: InstanceType<typeof web3.PublicKey>,
  apiConfigHash: Uint8Array,
  signaturesRequired: number
): Uint8Array {
  return keccak_256(
    Buffer.concat([
      FEED_ID_PREFIX,
      owner.toBuffer(),
      Buffer.from(apiConfigHash),
      Buffer.from([signaturesRequired]),
    ])
  );
}

function findFeedPda(feedId: Uint8Array): InstanceType<typeof web3.PublicKey> {
  return web3.PublicKey.findProgramAddressSync(
    [FEED_SEED, Buffer.from(feedId)],
    PROGRAM_ID
  )[0];
}

const feed = await program.account.feed.fetch(findFeedPda(feedId));
const raw = new BN(Buffer.from(feed.value), "be");      // big-endian bytes
const price = raw.toNumber() / 10 ** 6;                 // use your feed schema
```

Always apply a **staleness guard** before using the value:

```typescript theme={null}
const MAX_AGE_SECONDS = 60;
const age = Math.floor(Date.now() / 1000) - feed.canonicalTimestamp.toNumber();
if (age < 0 || age > MAX_AGE_SECONDS) {
  throw new Error(`Molpha feed is stale: ${age}s old`);
}
```

### Pattern B — from your Solana program

Pass the `Feed` PDA into your instruction and read it. Prefer typed deserialization: `value` is a Borsh `Vec<u8>` (4-byte length prefix + bytes), so fixed byte offsets after the discriminator are brittle.

```rust theme={null}
use anchor_lang::prelude::*;

pub const MOLPHA_PROGRAM_ID: Pubkey = pubkey!("MoLFGDpFoVnQgwbkTNScKPohCxhbfd61JjFrnotuwzh");
const FEED_SEED: &[u8] = b"molpha_feed";

#[derive(Accounts)]
#[instruction(feed_id: [u8; 32])]
pub struct UseMolphaFeed<'info> {
    /// CHECK: validated below to be the canonical Molpha feed PDA for `feed_id`.
    #[account(
        seeds = [FEED_SEED, feed_id.as_ref()],
        bump,
        seeds::program = MOLPHA_PROGRAM_ID,
    )]
    pub molpha_feed: AccountInfo<'info>,
}

pub fn use_molpha_feed(ctx: Context<UseMolphaFeed>, _feed_id: [u8; 32]) -> Result<()> {
    // Prefer: deserialize with the Molpha `Feed` type / `declare_program!` IDL helpers.
    // Manual Borsh layout after the 8-byte discriminator:
    //   feed_id[32] | value: Vec<u8> (u32 len + bytes) | value_kind:u8
    //   | canonical_timestamp:i64 LE | signatures_required:u8
    //   | signers_bitmap[32] | registry_version:u32 LE | bump:u8
    let data = ctx.accounts.molpha_feed.try_borrow_data()?;
    require!(data.len() > 8 + 32 + 4, MyError::InvalidFeed);
    let value_len = u32::from_le_bytes(data[40..44].try_into().unwrap()) as usize;
    let value_start = 44;
    let value_end = value_start + value_len;
    require!(data.len() >= value_end + 1 + 8, MyError::InvalidFeed);

    let value = &data[value_start..value_end];
    let ts_off = value_end + 1; // skip value_kind
    let canonical_ts =
        i64::from_le_bytes(data[ts_off..ts_off + 8].try_into().unwrap());

    let now = Clock::get()?.unix_timestamp;
    let age = now - canonical_ts;
    require!((0..=60).contains(&age), MyError::StaleData);
    let _ = value; // decode per your feed schema
    Ok(())
}
```

<Note>
  Account scalars (`registry_version`, `canonical_timestamp`) are little-endian Borsh. `value` is a length-prefixed byte vector (raw payload when ≤32 bytes, or a keccak digest when longer). Validate the PDA with `seeds::program = MOLPHA_PROGRAM_ID` so a caller can't substitute a look-alike account. `last_updated_slot` is emitted on the `FeedUpdated` event, not stored on the `Feed` account.
</Note>

If you prefer typed access, depend on the Molpha program crate, or use Anchor's `declare_program!` against the IDL for typed CPI and account helpers.

## Push then read

Use this when you want the Solana `Feed` account to become the source of truth.

```ts theme={null}
const { signature } = await sdk.solana.submitDataUpdate(result);
const feed = await sdk.solana.readFeed(result.feedId);
```

`submit_data_update` checks:

* aggregate Schnorr signature validity,
* signer bitmap and registry version,
* deterministic signer selection,
* monotonic `canonical_timestamp`,
* feed value encoding.

The submitter is not trusted. Any keeper, gateway, or user can submit; the program verifies the payload before accepting the write.

## Verify directly with `molpha-verifier`

Use this when your Solana program, native Rust service, CLI, or test harness needs to verify a signed payload without writing it to the Molpha `Feed` account.

Install:

```toml theme={null}
[dependencies]
molpha-verifier = "0.2"

# Optional Borsh support for DataUpdate.
# molpha-verifier = { version = "0.2", features = ["borsh"] }
```

For pre-resolved signers:

```rust theme={null}
use molpha_verifier::{verify_data_update, DataUpdate, SignerXy};

// ordered_signers: one (x, y) pair per set bit in signers_bitmap,
// ordered by ascending bitmap bit index.
verify_data_update(
    &payload,
    node_count,
    redundancy_buffer,
    &ordered_signers,
)?;
```

For Solana-program-style registry resolution:

```rust theme={null}
use molpha_verifier::{
    verify_data_update_resolved, DataUpdate, NodeEntry, RegistryView,
};

// 1. Owner-check and deserialize registry/node accounts in your program.
// 2. Copy plain fields into RegistryView and NodeEntry values.
// 3. Pass one NodeEntry per set bit in signers_bitmap.
verify_data_update_resolved(
    &payload,
    &registry_view,
    redundancy_buffer,
    clock_unix_timestamp,
    &node_entries,
)?;
```

The crate does not read accounts for you. Your program or service owns:

* account owner checks,
* account deserialization,
* node entry ordering,
* registry version selection,
* mapping `DataUpdateError` into your own error type.

## Read vs. verify

Reading the `Feed` gives you the last accepted on-chain value. Direct verification checks a payload you already have. In both paths, your application still enforces freshness, replay protection, expected `feedId`, quorum, and value bounds.
