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.2.1 generates TypeScript, Kotlin, Python, Go, C header-only, and
off-chain Rust clients, plus Codama-shaped JSON and Anchor-shaped IDL JSON for
downstream tooling. Account readers in every generated language assert the
right layout identity before decoding fields. Headered accounts compare the
8-byte Hopper LAYOUT_ID stored at bytes 4..12; compact accounts assert
exact size plus discriminator and expose the fingerprint from the manifest/IDL
layout identity.
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
Anchor wins social adoption because people know how to get from "program" to "client." Hopper needs a clean answer.
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 assert layout identity before reading fields, so stale clients
fail closed instead of silently mis-decoding account bytes. For compact layouts,
the generated TypeScript and Kotlin clients do not read a header fingerprint;
they check [disc][body] account bytes and return the manifest/IDL fingerprint
via layoutFingerprint(layout).
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
- Layout identity constants. Headered layouts call
assertLayoutId; compact layouts callassertCompactLayoutand decode fields from absolute offsets such as1..33and33..41.
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
Compact Kotlin account decoders use the same LayoutIdentity model as
TypeScript. They validate exact compact size plus discriminator, then decode
fields from the manifest offsets.
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 verifyLAYOUT_ID - 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 verifyLAYOUT_IDhopper_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
# Generate from piped manifest
hopper schema export --manifest | hopper client gen --ts -
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.
