ifx-sdk 0.1.2

Off-chain Ifx transaction builder (FrameScratch, wire encoding)
Documentation

Ifx Rust SDK (ifx-sdk)

← Ifx project homepage

ifx-sdk ifx-core

English | 中文

Rust off-chain client for Ifx: build ifx_create_frame, ifx_let, ifx_assert, ifx_patched_cpi, ifx_if_else, and related instructions. Does not wrap RPC or wallets — you get solana_sdk::instruction::Instruction values; your backend signs and sends.

Clusters: Default program_id is mainnet (DEFAULT_IFX_PROGRAM_ID = IFX_MAINNET_PROGRAM_ID). Local Surfpool / repo integration tests use IFX_LOCALNET_PROGRAM_ID. Devnet: IFX_DEVNET_PROGRAM_ID.

  • Crates.io name: ifx-sdk
  • Directory: rust-sdk/ (same convention as go-sdk/)
  • Shared types: ifx-core

Two layers

  1. FrameScratch — plan tape bindings (let_* / LetBuilder), emit ix_reset, ix_let, ix_assert, ix_cpi (ifx_patched_cpi), ix_if_else, …
  2. expr + ScratchValue — build on-chain Expr trees and Frame bindings (binding index, remaining accounts, types)

Prefer FrameScratch in application code; use build_ix_create_frame and friends when you need lower-level control.

Install

Current release: 0.1.2 on crates.io (ifx-core@0.1.1; mainnet default; aligned with @ifx-run/sdk@0.1.2 and go-sdk@v0.1.2).

cargo add ifx-sdk

Monorepo path dependency:

ifx-sdk = { path = "../rust-sdk" }

Pin in Cargo.toml:

ifx-sdk = { version = "0.1.2" }
ifx-core = { version = "0.1.1" }

Quick start

Tx 1 — Create a Frame (standalone)

Do not mix create with swap/settlement in the same transaction.

use ifx_sdk::constants::IFX_DEVNET_PROGRAM_ID;
use ifx_sdk::scratch::{FrameScratch, PlanNewFrameParams};

let frame_id = rand::random::<[u8; 32]>();
let plan = FrameScratch::plan_public_frame(PlanNewFrameParams {
    payer,
    frame_id: &frame_id,
    authority: payer, // ignored — public Frame uses Frame PDA as authority
    tape_len: 256,
    program_id: Some(IFX_DEVNET_PROGRAM_ID),
})?;
// Send plan.ix_create alone; persist plan.frame (pubkey) + tape_len (frame_id optional after create)
// plan.scratch.authority == plan.frame (public Frame — no extra signer on reset/let)

Optional — private / closeable Frame (authority: payer, signs reset/let; can close for rent): use plan_new_frame — frame-authority.md.

Tx 2 — Business (reset + let + assert / CPI)

use ifx_sdk::expr;
use ifx_sdk::scratch::FrameScratch;

let mut s = plan.scratch;
let reset_ix = s.ix_reset();
let target = s.let_const_u64(10)?;
let let_ix = s.ix_let_single(&target)?;
let assert_ix = s.ix_assert(&expr::non_zero(expr::r(&target)))?;
// Assemble transaction, sign, send

Production: logs, not Frame decode

Confirm behavior via Ifx transaction logs (conditions, CPI arms, patch offsets, assert results). On failure, use logs + error codes (errors).

Do not call decode_frame_account or tape readback helpers in production. These are for tests, examples, and local debugging only. Standalone business txs still start with ix_reset.

Single vs multi binding

Single: s.let_lamports(user)? → s.ix_let_single(&sv)? → later expr::r(&sv).

Multi: s.let_builder() → several lamports / spl_token_amount / … → b.build_ix()? (remaining deduped).

Conditions for ix_assert / ix_if_else: bool Expr or bool ScratchValue.

When to let

  • Persist: values used later by assert, CPI patches, or later lets
  • Skip persist: nest in LetEval, or compare inside ix_assert only

Fixed tape_len at create (no extend/shrink). See errors.md for IndexCapReached / TapeOutOfBounds.

Session helpers

Method Use
plan_new_frame New Frame: scratch + ix_create + PDA
plan_public_frame authority = Frame PDA (non-closeable; public scratch)
FrameScratch::new New session on an existing Frame (production path)

Public Frame check after decode (tests / debug only): public_frame_authority(frame) == decoded.authority.

(decode_frame_account — tests & debug only.)

Structured CPI & patched CPI

Use encode_cpi / build_ix_cpi with structured patches from ifx_core::structured_cpi, or raw patched CPI templates via cpi helpers. See structured-cpi-patches.md. Wire parity: rust-sdk/src/parity.rs.

Modules

Module Role
scratch Planner
frame / decode PDA, decode, readback (debug)
expr / binding / typed / wire_ix IR + wire
ix / cpi Instructions + CPI
let_bindings / let_builder Let helpers
frame_authority Public / private Frame authority helpers
constants Program IDs, discriminators, tape limits

Examples

See examples/README.md — reference planners under tests/common/planners/ (L0–L3); localnet e2e in tests/localnet.rs.

Errors

On-chain codes surface as transaction failures; match messages with error reference (6000–6035). Client-side planning errors: ScratchError.

Program IDs

IFX_MAINNET_PROGRAM_ID · IFX_DEVNET_PROGRAM_ID · IFX_LOCALNET_PROGRAM_ID · DEFAULT_IFX_PROGRAM_ID. Set once on FrameScratch via PlanNewFrameParams::program_id.

Tests

Unit + wire parity (no RPC):

cargo test -p ifx-sdk

Localnet integration (Surfpool / anchor test validator; skips when RPC unavailable):

export ANCHOR_PROVIDER_URL=http://127.0.0.1:8899
export ANCHOR_WALLET=~/.config/solana/id.json
cargo test -p ifx-sdk --test localnet -- --nocapture

Or from repo root: npm run rust:test / npm run rust:test:integration.

Other languages

TypeScript: @ifx-run/sdk. Go: go-sdk. Same on-chain wire. See repo docs/ for orchestration patterns.