تخطَّ إلى المحتوى

Build on OpenFiat

There are two surfaces to build against, and they work differently. The peer-to-peer marketplace — advertisements, reservations, reputation, oracles — is JSON-RPC against any node. Money movement is Anchor programs on Solana. This walks through the first surface, then shows where the second one starts.

المحطة 1 من 1 · Integrate the protocol

قبل أن تبدأ

  • A node to talk to — run one locally, or use any node whose JSON-RPC endpoint you can reach
  • Rust, or Node.js 20+ for TypeScript — those are the two SDKs that actually work today
  • No API key, and no account. Writes authenticate with your own signature, so nobody issues you credentials
  • A Solana wallet only if you plan to touch the on-chain programs; the marketplace surface does not need one

الخطوات

  1. 01

    Get a node to talk to

    Everything a client does goes through a node. One process serves the whole surface on a single HTTP port: JSON-RPC at POST /rpc, a WebSocket event stream at GET /ws, plus /health, /metrics, /openrpc.json and /docs. Note that you give an SDK the base URL — it appends /rpc itself, so passing a URL that already ends in /rpc will not work.

    shell
    # From a clone of openfiat-core. The package is openfiat-cli;
    # the binary it produces is openfiat-node.
    cargo run -p openfiat-cli -- --rpc-bind-address 127.0.0.1:7080
    
    # In another shell:
    curl -s http://localhost:7080/health
    # ok
  2. 02

    See what the node can actually do

    Before writing any code, read the node's own description of itself. It serves an OpenRPC document generated from its live method table, so it lists exactly the methods that this build of the node implements — it cannot drift from the code the way a hand-written API page does. There is also a self-contained reference page that loads that document and runs methods against /rpc in the browser.

    shell
    # Every method this node serves, straight from its own method table.
    curl -s http://localhost:7080/openrpc.json | jq '.methods[].name'
    
    # The same document, rendered as an interactive reference you can
    # call methods from — browse to:
    #   http://localhost:7080/docs
  3. 03

    Add an SDK

    Neither SDK is published to crates.io or npm yet — the repository has no publish workflow, so depend on it from Git and pin a commit. That is exactly what the reference web app does. You can also just clone the monorepo and run its examples in place, which is the fastest way to see a real round trip.

    Cargo.toml
    [dependencies]
    openfiat-sdk = { git = "https://github.com/OpenFiat-org/openfiat-sdks", rev = "<commit>", package = "openfiat-sdk" }
    tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
    package.json
    {
      "dependencies": {
        "@openfiat/sdk": "github:OpenFiat-org/openfiat-sdks#<commit>&path:typescript"
      }
    }
    Or run the examples in place
    git clone https://github.com/OpenFiat-org/openfiat-sdks
    cd openfiat-sdks
    
    # Rust: registers a service provider and publishes a signed rate.
    (cd rust && cargo run --example oracle_provider)
    
    # TypeScript: the same example, same default endpoint.
    (cd typescript && pnpm install && pnpm tsx examples/oracle_provider.ts)
  4. 04

    Make a read call

    Reads need no key and no signature. The two SDKs shape this differently: in Rust the typed methods hang off Client itself, while in TypeScript they are functions in a per-domain namespace that take the client as their first argument.

    Rust
    use openfiat_sdk::{Client, ClientConfig};
    
    // The base URL — the SDK appends /rpc itself.
    let client = Client::new(ClientConfig {
        endpoint: "http://localhost:7080".to_string(),
        ..ClientConfig::default()
    });
    
    println!("node version: {}", client.get_version().await?);
    
    let ads = client.get_advertisements().await?;
    println!("{} advertisements on this node", ads.len());
    TypeScript
    import { Client, advertisements, node } from "@openfiat/sdk";
    
    const client = new Client({ endpoint: "http://localhost:7080", timeoutMs: 30_000 });
    
    console.log("node version:", await node.getVersion(client));
    
    const ads = await advertisements.getAdvertisements(client);
    console.log(ads.length, "advertisements on this node");
  5. 05

    Submit a signed write

    Every write follows one model: you build the domain's unsigned event, the SDK signs it with your Ed25519 key, JSON-encodes it, and submits it as a sendX call. The node verifies that signature itself, which is why there is no API key anywhere — authority comes from the key, not from an account someone granted you. Use a persistent wallet.json rather than a fresh key each run: reputation attaches to the key, so a new one starts from nothing.

    Rust
    use openfiat_sdk::wallet::{keypair_from_wallet, solana_keyfile};
    
    // Solana CLI wallet.json format — the same file the node itself uses.
    let wallet = solana_keyfile::load("/etc/openfiat/wallet.json")?;
    let keypair = keypair_from_wallet(&wallet);
    
    // Every send_* takes the domain's unsigned event plus your keypair.
    // See the merchant guide for how to fill in an AdvertisementCreate.
    let id = client.send_advertisement_create(create, &keypair).await?;
    TypeScript
    import { advertisements } from "@openfiat/sdk";
    // File I/O lives in a separate entry point, so the main one stays
    // browser- and edge-safe.
    import { loadWalletFile } from "@openfiat/sdk/node";
    
    const wallet = await loadWalletFile("/etc/openfiat/wallet.json");
    
    const id = await advertisements.sendAdvertisementCreate(client, create, wallet.keypair);
  6. 06

    Where the second surface starts

    Anything that moves money is an Anchor program on Solana, not a node call: escrow, staking, governance. The SDKs give you typed instruction builders for all three, with PDA derivation and Anchor's wire format handled for you — but they deliberately stop there. They never construct, sign or submit a Solana transaction on your behalf. You assemble the instructions, sign with your own Solana keypair, and relay the transaction through a node so it reaches the chain the same way whether that node has its own Solana RPC connection or only gossip.

    Rust
    use openfiat_sdk::onchain::{Role, staking};
    
    // Returns a plain solana_instruction::Instruction — nothing is signed
    // or submitted here.
    let ix = staking::stake_ix(&owner, Role::Arbitrator, &mint, &from, amount);
    
    // Build a transaction with this blockhash, sign it yourself, then relay:
    let blockhash = client.get_latest_blockhash().await?.blockhash;
    client.send_transaction(&signed_tx).await?;
    Devnet program ids
    # Deployed to devnet only — there is no mainnet deployment.
    escrow      HaPpM1QYM3dKp3sX7zhEdft9hB6ncu6xfALAbkyQChQP
    staking     HYEXk8XQukBkZbiYB33JyVefQDxqyCpPudad3wBCyYmx
    governance  AVJfKUjHsizkGGUy8sdz4Xma2hVgmgvgg8GmUMs8E4eE
  7. 07

    Pick a language with your eyes open

    The two SDKs are not at parity, and the gap will decide your language for you. Rust has typed methods for every domain the node serves — sixteen of them, including disputes, governance, settlement, trade, reputation and identity. TypeScript covers seven: advertisements, chain, node, notifications, oracles, providers and reservations, plus the on-chain instruction builders. Anything outside those seven still works from TypeScript, but you call it through the client's generic call method and type the result yourself. Python is a typed stub whose one method raises NotImplementedYetError, and the Go, Swift, Kotlin and C# directories contain a README and nothing else. Neither SDK wraps the WebSocket stream at /ws, so use a plain WebSocket client if you want live events.

    TypeScript — a domain without a typed module
    // getDisputes has no typed wrapper in the TypeScript SDK yet.
    // call() reaches any method the node serves; you supply the types.
    const disputes = await client.call<Record<string, never>, unknown[]>(
      "getDisputes",
      {},
    );