hashi
  • Welcome
  • Introduction
    • What is Hashi?
    • Why Hashi?
    • Key Features
    • The SSN
  • Getting Started
    • How Hashi Works
    • Quick Start
      • Verifying foreign event
        • Setting the scene
        • Getting the Event Proof
        • Validating your statements
      • Reading foreign state
        • Setting the scene
        • Getting the Storage Proof
        • Validating your statements
      • Pushing a message
        • Writing your message sending contract
        • Implementing your callback
        • Waiting for the cross-chain execution
  • Core Concepts
    • Block Header Relaying
    • Message Dispatching
    • Oracles
    • Additive Security Model
  • Deployments
    • Blockchains
    • Oracles
  • Smart Contracts
    • Key Contracts
    • HashiProver API
  • TOOLS
    • SP1 storage proof verifier
    • Solana integration
  • APPS / INTEGRATIONS
    • Safe Crosschain
    • Aragon UCG
    • Openfort Chain Abstraction
  • META
    • Developer resources
    • Audits
    • Explorer
    • Community
Powered by GitBook
On this page
  • Overview
  • How to read a Solana account from an EVM chain
Export as PDF
  1. TOOLS

Solana integration

PreviousSP1 storage proof verifierNextSafe Crosschain

Last updated 3 months ago

Overview

The integration is still experimental, and some breaking changes may occur.

Hashi’s core functionality is to propagate block headers across multiple chains, enabling cross-chain state verification. On EVM-based chains, this can be done in a trustless manner because the EVM exposes the last 256 block headers. However, this approach is not feasible on Solana, as the Solana VM only provides slot hashes, which do not include commitments that would allow verification of account states (as opposed to the state_root on Ethereum).

Nevertheless, Solana’s VM does allow reading account states (such as data, address, lamports, owner, and rent_epoch). To make use of this capability, we developed a program called . Snapshotter allows the propagation of the hash of each subscribed account’s state to other chains via Hashi reporters.

Technically, whenever the subscribe function is called, the hash of the specified account’s state is added to a Merkle tree. Once a predefined batch size is reached (i.e., a certain number of subscriptions have occurred), anyone can call calculate_root to compute and store the Merkle root on-chain (AKA accounts_root). At that point, any Hashi-compatible reporter ( is an example of a Hashi-compatible reporter) can invoke dispatch_root to propagate the root to other blockchains, thereby synchronizing the account state across multiple ecosystems. Finally, on the destination chain, you can verify an account simply by verifying a Merkle proof.

How to read a Solana account from an EVM chain

Suppose you need to read the Solana account data from Base. The first step is to let the Snapshotter know that you want the System Program account’s hash to be included in the next accounts_root. To do this, simply call subscribe. Full example .

const [configKey] = PublicKey.findProgramAddressSync([Buffer.from("config", "utf-8")], snapshotter.programId)
const systemProgram = new PublicKey("11111111111111111111111111111111")
await snapshotter.methods
    .subscribe(systemProgram)
    .accounts({
        config: configKey,
     } as any)
    .rpc()

Once the number of subscribed accounts reaches , anyone can call calculate_root, which computes the Merkle root of the new batch and stores it so reporters can easily retrieve it when broadcasting to other chains. Because each new batch’s root (batch_accounts_root) must be combined with the existing accounts_root (to preserve previously included accounts), we implemented a custom “.” This specialized Merkle tree allows us to merge all batch_accounts_root values into a single accounts_root without losing any historical data. Full example .

const [configKey] = PublicKey.findProgramAddressSync([Buffer.from("config", "utf-8")], snapshotter.programId)
const batch = new anchor.BN(0) // must be equal to the current batch
await snapshotter.methods
  .calculateRoot(batch)
  .accounts({
    config: configKey,
  } as any)
  .remainingAccounts(batchAccounts)
  .rpc()
const tracker = await getProgramSequenceTracker(provider.connection, reporter.programId, CORE_BRIDGE_PID)
const message = deriveWormholeMessageKey(reporter.programId, tracker.sequence + 1n)
const wormholeAccounts = getPostMessageCpiAccounts(
  reporter.programId,
  CORE_BRIDGE_PID,
  provider.publicKey,
  message,
)
const [configKey] = PublicKey.findProgramAddressSync([Buffer.from("config", "utf-8")], reporter.programId)
const [snapshotterConfigkey] = PublicKey.findProgramAddressSync(
  [Buffer.from("config", "utf-8")],
  snapshotter.programId,
)

await reporter.methods
  .dispatchRoot()
  .accounts({
    config: configKey,
    wormholeProgram: new PublicKey(CORE_BRIDGE_PID),
    snapshotterConfig: snapshotterConfigkey,
    ...wormholeAccounts,
  } as any)
  .rpc()

Once the accounts_root is successfully stored in the corresponding adapter(s) on Base, you can read the account data by calling HashiProver.verifyForeignSolanaAccount from any contract.

pragma solidity ^0.8.20;

import { HashiProver } from "../prover/HashiProver.sol";
import "../prover/HashiProverStructs.sol";

contract SolanaAccountReader is HashiProver {
    constructor(address shoyuBashi) HashiProver(shoyuBashi) {}

    function getSolanaAccount(SolanaAccountProof calldata proof) external view returns (bytes memory) {
        return verifyForeignSolanaAccount(proof);
    }
}

Once the accounts_root is updated, you can transmit it to other chains simply by calling dispatch_root. In this example, the is used to dispatch the root, but any compatible reporter will work. Full example .

For a complete example of how to read a Solana account data, refer to this .

Snapshotter
This
System Program
here
BATCH_SIZE
Batch Merkle Tree
here
Wormhole reporter
here
example