Client generation
Generate TypeScript, Kotlin, Python, Go, C, and off-chain Rust clients from one Hopper manifest.
hopper client gen --ts <manifest.json>-> TypeScript SDKhopper client gen --kt <manifest.json>-> Kotlin SDK (org.sol4k)hopper client gen --py <manifest.json>-> Python SDK (stdlib-only)hopper client gen --go <manifest.json>-> Go SDK (stdlib-only)hopper client gen --c <manifest.json>-> C header-only clienthopper compile --emit rust-client <manifest.json>-> Rust off-chain SDK
Hopper 0.3.1 generates TypeScript, Kotlin, Python,
Go, C header-only, and off-chain Rust clients, plus Hopper public IDL, Codama
JSON, and a lossless-only Solana IDL v0.1.0 projection. compile --emit idl
emits Hopper's public subset; Solana IDL is the separate schema export --anchor-idl <manifest> --program-id <pubkey> path. That projection preserves
Hopper's wire discriminators and declares custom hopper-zero-copy-v1 account
serialization, so account decoding remains Hopper-aware. It refuses bounded
encodings, remaining-account contracts, unresolved or unsafe PDA seeds,
unresolved fixed addresses, and ambiguous discriminator prefixes that cannot
be represented faithfully. Headered account readers in every generated
language assert the 8-byte Hopper LAYOUT_ID before decoding fields. Fixed
compact readers require exact manifest size plus discriminator;
compact-dynamic readers require the manifest's minimum prefix size plus
discriminator. Compact bytes do not contain a fingerprint, so the generated
LAYOUT_ID remains manifest/IDL identity metadata. Generated readers decode
declared fixed fields; application-specific dynamic-tail semantics remain a
separate validation step.
Copyable generated-client flows
Write all generated artifacts to files during release verification:
hopper compile --emit ts --package hopper-token-2022-vault --out target/clients/vault.ts --force
hopper compile --emit kt --package hopper-token-2022-vault --out target/clients/Vault.kt --force
hopper compile --emit py --package hopper-token-2022-vault --out target/clients/vault_client.py --force
hopper compile --emit go --package hopper-token-2022-vault --out target/clients/vault_client.go --force
hopper compile --emit c --package hopper-token-2022-vault --out target/clients/vault_client.h --force
hopper compile --emit rust-client --package hopper-token-2022-vault --out target/clients/vault_client.rs --force
Generate user-facing surfaces from the same manifest:
hopper actions gen --program examples/hopper-token-2022-vault/hopper.manifest.json --out target/actions
hopper mobile gen --program examples/hopper-token-2022-vault/hopper.manifest.json --target kotlin --out target/mobile-kotlin
hopper mobile gen --program examples/hopper-token-2022-vault/hopper.manifest.json --target react-native --out target/mobile-rn
hopper test-gen security --program examples/hopper-token-2022-vault/hopper.manifest.json --out target/security_matrix.rs
Every command above consumes the manifest. That keeps account layout identity, instruction tags, client decoders, Actions route scaffolds, mobile binding stubs, and generated security test matrices on the same source of truth.
Motivation
A program needs clients that agree with its instruction and account contracts.
The client generator reads a Hopper ProgramManifest and emits typed SDKs that
frontends, bots, scripts, and tests can drop into their project. All generated
account decoders fail closed before reading fields: headered layouts compare
the 8-byte LAYOUT_ID in account bytes, while compact layouts compare the
discriminator and either exact fixed size or a compact-dynamic minimum prefix.
They retain the manifest fingerprint as external identity metadata.
TypeScript Generation
What it generates
=== types.ts ===
=== accounts.ts ===
=== instructions.ts ===
=== events.ts ===
=== index.ts ===
The CLI prints a file-marked bundle to stdout. Each section can be split into its own file, or consumed by a generation pipeline.
Type mapping
| Hopper canonical type | TypeScript type | Encoding |
|---|---|---|
u8 |
number |
1 byte LE |
u16 |
number |
2 bytes LE |
u32 |
number |
4 bytes LE |
u64 |
bigint |
8 bytes LE |
u128 |
bigint |
16 bytes LE |
i8 |
number |
1 byte LE signed |
i16 |
number |
2 bytes LE signed |
i32 |
number |
4 bytes LE signed |
i64 |
bigint |
8 bytes LE signed |
bool |
boolean |
1 byte (0/1) |
Pubkey |
PublicKey |
32 bytes |
[u8; N] |
Uint8Array |
N bytes |
Account decoders
Each account type gets:
- A TypeScript interface with typed fields
- A
decode<Name>(data: Buffer): <Name>function - A discriminator constant for account identification
Instruction builders
Each instruction gets:
- An
<InstructionName>Argsinterface for typed arguments - An
<InstructionName>Accountsinterface for required accounts - A
create<InstructionName>Instruction(args, accounts, programId)function that returns aTransactionInstruction
Events
Each event gets:
- A TypeScript interface with typed fields
- A
decode<Name>Event(data: Buffer): <Name>Eventfunction
Kotlin Generation
Kotlin generation is implemented today. The generator emits a file-marked
bundle targeting org.sol4k primitives:
Types.ktwith header and discriminator helpersAccounts.ktwith data classes and decodersInstructions.ktwith typed argument/account classes and buildersEvents.ktwith event data classes and decoders
Encoding and decoding use explicit little-endian ByteBuffer operations, so
the generated Kotlin stays close to Hopper's wire model.
Python Generation
Python generation emits a single stdlib-only module:
- dataclasses for account layouts and events
decodeclassmethods that verify layout identity first- segment-aware field readers for partial account inspection
build_<instruction>helpers that return raw instruction-data bytes
The generated module intentionally does not pick a Solana transport library.
Callers can pass the emitted bytes to solders, solana-py, or their own RPC
stack.
Go Generation
Go generation emits a single stdlib-only package with:
Pubkey,AccountMeta, andInstructiontransport-neutral structs- typed account structs and
Decode<Name>helpers that verify headeredLAYOUT_ID, fixed compact exact size plus discriminator, or compact-dynamic minimum prefix size plus discriminator - typed
<Instruction>Args/<Instruction>Accountsstructs Build<Instruction>Instructionhelpers with discriminator-prefixed data- event structs and decoders keyed by event tag
The generated package deliberately avoids choosing a Go Solana SDK. Callers can
adapt the emitted Instruction into their RPC or transaction-building library.
C Generation
C generation emits a single header-style client with:
- fixed-width integer and
HopperPubkeytypes hopper_decode_<layout>account decoders that verify headeredLAYOUT_ID, fixed compact exact size plus discriminator, or compact-dynamic minimum prefix size plus discriminatorhopper_build_<instruction>_datapayload buildershopper_<instruction>_account_metasaccount metadata helpers- event decoders and explicit
HopperClientErrorreturn codes
The header is dependency-light (stdint.h, stdbool.h, stddef.h, string.h)
and leaves transaction transport to the embedding application.
Rust Client Generation
hopper compile --emit rust-client emits an off-chain Rust client using
Solana SDK types. It is separate from hopper compile --emit rust, which emits
the lowered Hopper runtime preview for auditing macro expansion, accessors, and
offset paths.
Usage
# Generate from manifest JSON
hopper client gen --ts @my-program.manifest.json
# Generate Kotlin client bundle
hopper client gen --kt @my-program.manifest.json
# Generate Python client module
hopper client gen --py @my-program.manifest.json
# Generate Go client package
hopper client gen --go @my-program.manifest.json
# Generate C client header
hopper client gen --c @my-program.manifest.json
# Generate off-chain Rust client
hopper compile --emit rust-client @my-program.manifest.json
# Normalize a manifest, then generate from the resulting file
hopper schema export --manifest @my-program.manifest.json > normalized.manifest.json
hopper client gen --ts @normalized.manifest.json
Architecture
ProgramManifest
├─ hopper-schema::clientgen::TsClientGen
│ ├─ types.ts
│ ├─ accounts.ts
│ ├─ instructions.ts
│ ├─ events.ts
│ └─ index.ts
├─ hopper-schema::clientgen::KtClientGen
│ ├─ Types.kt
│ ├─ Accounts.kt
│ ├─ Instructions.kt
│ └─ Events.kt
├─ hopper-schema::python_client::PyClientGen
│ └─ <program>_client.py
├─ hopper-schema::go_client::GoClientGen
│ └─ client.go
├─ hopper-schema::c_client::CClientGen
│ └─ client.h
└─ hopper-schema::rust_client::RsClientGen
└─ client.rs
The generator operates on ProgramManifest directly. It does NOT
parse JSON. The CLI is responsible for loading the manifest; the
generator formats TypeScript, Kotlin, Python, Go, C, and Rust output bundles.
