Documentation
¶
Overview ¶
Package encoding implements Solana's binary wire formats.
Three formats live here:
- bincode: Rust bincode-crate output. Vec / String use u64 LE length prefixes. Used by Solana built-ins (System nonce, Vote, Stake).
- Borsh: Vec / String use u32 LE length prefixes. Used by Anchor programs, SPL Token-2022 extension state, Token Metadata, Address Lookup Table state, and most modern third-party programs.
- shortvec: compact-u16 length prefix. Used inside transaction and message bodies (signature count, account-keys count, etc.).
Three layers of API, by use case:
- One-shot reflection: BinDecodeTo / BorshDecodeTo. Pick by program; see those godocs for the full picker. Cheapest to write, slowest to run. The two formats only differ in slice / string length prefix width — structs without slice or string fields decode the same with either function.
- Hand-written hot-path decoding: NewReader + r.U64() / r.Bytes32() / r.Shortvec(). Canonical path for performance-sensitive code and for wire layouts that reflection can't express (TLV, COption<Pubkey>, OptionalNonZeroPubkey, custom enum tags).
- Hand-written instruction-data builders: NewEncoder + chained New().U8(tag).U64(amount).Bytes(). The convention every program in programs/* follows.
The Decoder type underpins the reflective entry points and the Reader API; it never copies the input buffer.
Index ¶
- Variables
- func BinDecodeTo(data []byte, v any) error
- func BorshDecodeTo(data []byte, v any) error
- func DecodeShortvec(b []byte) (uint16, int, error)
- func EncodeShortvec(b []byte, v uint16) int
- type Decoder
- func (d *Decoder) DecodeFast(v any) error
- func (d *Decoder) DecodeTo(v any) error
- func (d *Decoder) PeekUint8() (uint8, error)
- func (d *Decoder) Pos() int
- func (d *Decoder) ReadBytes(n int) ([]byte, error)
- func (d *Decoder) ReadInt8() (int8, error)
- func (d *Decoder) ReadInt16() (int16, error)
- func (d *Decoder) ReadInt32() (int32, error)
- func (d *Decoder) ReadInt64() (int64, error)
- func (d *Decoder) ReadShortvec() (uint16, error)
- func (d *Decoder) ReadU128() (U128, error)
- func (d *Decoder) ReadU256() (U256, error)
- func (d *Decoder) ReadUint8() (uint8, error)
- func (d *Decoder) ReadUint16() (uint16, error)
- func (d *Decoder) ReadUint32() (uint32, error)
- func (d *Decoder) ReadUint64() (uint64, error)
- func (d *Decoder) Remaining() int
- func (d *Decoder) UseBorsh() *Decoder
- type Encoder
- func (e *Encoder) Bool(v bool) *Encoder
- func (e *Encoder) Bytes() []byte
- func (e *Encoder) I8(v int8) *Encoder
- func (e *Encoder) I16(v int16) *Encoder
- func (e *Encoder) I32(v int32) *Encoder
- func (e *Encoder) I64(v int64) *Encoder
- func (e *Encoder) Len() int
- func (e *Encoder) OptBool(v *bool) *Encoder
- func (e *Encoder) OptI64(v *int64) *Encoder
- func (e *Encoder) OptRaw(b []byte) *Encoder
- func (e *Encoder) OptU8(v *uint8) *Encoder
- func (e *Encoder) OptU32(v *uint32) *Encoder
- func (e *Encoder) OptU64(v *uint64) *Encoder
- func (e *Encoder) Raw(b []byte) *Encoder
- func (e *Encoder) StrU64(s string) *Encoder
- func (e *Encoder) U8(v uint8) *Encoder
- func (e *Encoder) U16(v uint16) *Encoder
- func (e *Encoder) U32(v uint32) *Encoder
- func (e *Encoder) U64(v uint64) *Encoder
- func (e *Encoder) U128c(v U128) *Encoder
- func (e *Encoder) U256c(v U256) *Encoder
- func (e *Encoder) WriteBytes(b []byte)
- func (e *Encoder) WriteInt8(v int8)
- func (e *Encoder) WriteInt16(v int16)
- func (e *Encoder) WriteInt32(v int32)
- func (e *Encoder) WriteInt64(v int64)
- func (e *Encoder) WriteShortvec(v uint16)
- func (e *Encoder) WriteU128(u U128)
- func (e *Encoder) WriteU256(u U256)
- func (e *Encoder) WriteUint8(v uint8)
- func (e *Encoder) WriteUint16(v uint16)
- func (e *Encoder) WriteUint32(v uint32)
- func (e *Encoder) WriteUint64(v uint64)
- type Reader
- func (r *Reader) Bool() bool
- func (r *Reader) Bytes(n int) []byte
- func (r *Reader) Bytes32() [32]byte
- func (r *Reader) Bytes64() [64]byte
- func (r *Reader) Decoder() *Decoder
- func (r *Reader) Done() error
- func (r *Reader) Err() error
- func (r *Reader) I8() int8
- func (r *Reader) I16() int16
- func (r *Reader) I32() int32
- func (r *Reader) I64() int64
- func (r *Reader) Pos() int
- func (r *Reader) Read(out []byte)
- func (r *Reader) Remaining() int
- func (r *Reader) Skip(n int)
- func (r *Reader) StrU64() string
- func (r *Reader) U8() uint8
- func (r *Reader) U16() uint16
- func (r *Reader) U32() uint32
- func (r *Reader) U64() uint64
- func (r *Reader) U128() U128
- func (r *Reader) U256() U256
- type TrailingBytesError
- type U128
- func (u U128) BigInt() *big.Int
- func (u U128) Hi() uint64
- func (u U128) IsZero() bool
- func (u U128) Lo() uint64
- func (u U128) MarshalJSON() ([]byte, error)
- func (u U128) MarshalText() ([]byte, error)
- func (u *U128) SetBigInt(x *big.Int) error
- func (u *U128) SetLoHi(lo, hi uint64)
- func (u *U128) SetUint64(v uint64)
- func (u U128) String() string
- func (u *U128) UnmarshalJSON(b []byte) error
- func (u *U128) UnmarshalText(b []byte) error
- type U256
- func (u U256) BigInt() *big.Int
- func (u U256) IsZero() bool
- func (u U256) MarshalJSON() ([]byte, error)
- func (u U256) MarshalText() ([]byte, error)
- func (u *U256) SetBigInt(x *big.Int) error
- func (u *U256) SetWord(i int, v uint64)
- func (u U256) String() string
- func (u *U256) UnmarshalJSON(b []byte) error
- func (u *U256) UnmarshalText(b []byte) error
- func (u U256) Word(i int) uint64
- type Unmarshaler
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidShortvec = errors.New("solana/encoding: invalid shortvec")
ErrInvalidShortvec is returned when a compact-u16 encoding is structurally invalid: either the third byte sets the continuation bit (attempting a 4-byte encoding) or its value would overflow the destination uint16. The canonical Solana shortvec is 1-3 bytes and the third byte must be in the range 0x00-0x03.
var ErrShortBuffer = errors.New("solana/encoding: short buffer")
ErrShortBuffer is returned when a Decoder runs out of bytes mid-value or when a caller asks for a negative read length.
Functions ¶
func BinDecodeTo ¶ added in v0.1.3
BinDecodeTo decodes data into v using Rust bincode wire conventions.
What's different from Borsh (BorshDecodeTo) — exactly two field shapes:
- []T / Vec<T> length: 8-byte u64 LE prefix (Borsh: 4-byte u32)
- string / String length: 8-byte u64 LE prefix (Borsh: 4-byte u32)
Everything else (fixed arrays, primitives, *T / Option<T>, struct concatenation) is identical, so structs with no slice or string fields decode the same either way.
Pick BinDecodeTo for accounts produced by Solana built-ins serialised with the Rust bincode crate:
- System Program nonce accounts
- Vote Program state
- Stake Program state
Most modern Solana programs use Borsh; reach for BorshDecodeTo by default and only fall back to BinDecodeTo for the built-ins listed above.
For fixed-shape, performance-sensitive decoders prefer the Reader API.
func BorshDecodeTo ¶
BorshDecodeTo decodes data into v using Borsh wire conventions.
What's different from bincode (BinDecodeTo) — exactly two field shapes:
- []T / Vec<T> length: 4-byte u32 LE prefix (bincode: 8-byte u64)
- string / String length: 4-byte u32 LE prefix (bincode: 8-byte u64)
Everything else (fixed [N]byte arrays, all primitives, *T / Option<T> as 1-byte tag + optional payload, structs as concatenated fields) is byte- for-byte identical between the two — so structs with no slice or string fields decode the same either way.
Pick BorshDecodeTo for accounts produced by:
- Anchor programs (#[derive(BorshSerialize, BorshDeserialize)])
- SPL Token-2022 extension state
- Token Metadata
- Address Lookup Table state
- most modern third-party programs
If unsure, check the Rust source: BorshSerialize/BorshDeserialize derives mean Borsh; the bincode crate or solana_program::serialization means BinDecodeTo.
func DecodeShortvec ¶
DecodeShortvec reads a compact-u16 from b and returns the decoded value, the number of bytes consumed, and an error. The error is ErrShortBuffer when b is truncated mid-value, and ErrInvalidShortvec when the encoding is structurally invalid (fourth-byte continuation or uint16 overflow).
func EncodeShortvec ¶
EncodeShortvec writes v into b using Solana's compact-u16 (shortvec) encoding and returns the number of bytes written. b must have length at least 3. The encoding is 1-3 bytes: each byte holds 7 data bits in the low bits and a continuation flag in the high bit.
Types ¶
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is a zero-copy reader for Solana's little-endian wire format. It holds a cursor into a caller-owned byte slice; ReadBytes returns slices aliasing that buffer.
func NewDecoder ¶
NewDecoder returns a Decoder that reads from b. The caller retains ownership of b and must not mutate it for the lifetime of the Decoder or of any slice returned by ReadBytes.
func (*Decoder) DecodeFast ¶
DecodeFast is Decode using a cached, compiled per-type plan.
func (*Decoder) DecodeTo ¶
DecodeTo decodes the next value from the stream into v, which must be a non-nil pointer. It is the entry point for reflection-based decoding.
If the target type (or *T) implements Unmarshaler, the plan-cache emits a single dispatch op and reflection never descends into the type's fields.
Otherwise the supported kinds are: uint8/16/32/64, int8/16/32/64, bool, [N]byte and [N]T fixed arrays, []byte and []T slices (length-prefixed), string (length-prefixed), pointer (bincode Option: 1-byte tag + optional payload), and struct (recursively, all exported fields).
The length prefix is bincode's u64 by default; call UseBorsh on the decoder to switch to Borsh's u32. Bespoke widths (shortvec, u8, …) are not exposed via tags — hand-write the instruction with Reader / Encoder.
func (*Decoder) ReadBytes ¶
ReadBytes returns a zero-copy subslice of n bytes aliasing the underlying buffer. The returned slice is valid only as long as the buffer passed to NewDecoder is valid, and its capacity is clamped to n so that a rogue append cannot overwrite neighbouring bytes.
func (*Decoder) ReadShortvec ¶
ReadShortvec decodes a Solana compact-u16 length prefix and advances the cursor by the number of bytes consumed. On error the cursor is left unchanged.
func (*Decoder) ReadU128 ¶
ReadU128 reads a 16-byte little-endian U128. The bytes are copied, so the returned value is independent of the Decoder's underlying buffer.
func (*Decoder) ReadUint16 ¶
ReadUint16 reads a little-endian uint16.
func (*Decoder) ReadUint32 ¶
ReadUint32 reads a little-endian uint32.
func (*Decoder) ReadUint64 ¶
ReadUint64 reads a little-endian uint64.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is an append-based writer for Solana's little-endian wire format. It holds a growable byte buffer and never reflects on its arguments.
func New ¶
func New() *Encoder
New returns an Encoder with a 128-byte default capacity — large enough that essentially every Solana instruction encodes without a single buffer re-allocation. Use this when you don't care to compute the exact instruction-data size yourself; reach for NewEncoder(N) only when N is much larger than 128 (e.g. a variable-length address list).
func NewEncoder ¶
NewEncoder returns an Encoder whose internal buffer starts with the given initial capacity. Passing 0 is legal — the buffer grows on first write — but providing an estimate avoids the small re-alloc cost.
func (*Encoder) Bytes ¶
Bytes returns the accumulated bytes. The returned slice aliases the internal buffer and is valid only until the next write.
func (*Encoder) OptRaw ¶
OptRaw writes a Rust Option<[N]byte>: 1-byte tag, then b verbatim when non-nil. Pass nil to encode None. The slice length is not length-prefixed: this is intended for fixed-size optional fields such as Option<Pubkey> via OptRaw(pk[:]).
func (*Encoder) Raw ¶
Raw appends b verbatim (no length prefix). Use for fixed-size byte arrays such as pubkeys (caller passes pk[:]) or pre-encoded discriminators.
func (*Encoder) StrU64 ¶ added in v0.1.3
StrU64 writes a bincode string: u64 little-endian length, then UTF-8 bytes. For Borsh strings (u32 length) compose e.U32(uint32(len(s))).Raw([]byte(s)).
func (*Encoder) WriteBytes ¶
WriteBytes appends b verbatim, without any length prefix.
func (*Encoder) WriteInt16 ¶
WriteInt16 appends a little-endian int16.
func (*Encoder) WriteInt32 ¶
WriteInt32 appends a little-endian int32.
func (*Encoder) WriteInt64 ¶
WriteInt64 appends a little-endian int64.
func (*Encoder) WriteShortvec ¶
WriteShortvec appends a Solana compact-u16 length prefix (1-3 bytes), encoding v using EncodeShortvec.
func (*Encoder) WriteUint8 ¶
WriteUint8 appends a single byte.
func (*Encoder) WriteUint16 ¶
WriteUint16 appends a little-endian uint16.
func (*Encoder) WriteUint32 ¶
WriteUint32 appends a little-endian uint32.
func (*Encoder) WriteUint64 ¶
WriteUint64 appends a little-endian uint64.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader is a sticky-error wrapper around Decoder for chained, fluent decoding of fixed-shape Solana wire data. Each accessor reads one field; the first short-buffer (or invalid-shortvec) error is stored on the Reader and silences subsequent reads — so callers can sequence the happy path without per-call err checks and check Err() once at the end.
r := encoding.NewReader(data)
flag := r.U32()
var pk [32]byte
r.Read(pk[:])
supply := r.U64()
decimals := r.U8()
if err := r.Err(); err != nil {
return nil, err
}
The underlying Decoder is exposed via Decoder() for callers who need shortvec, position, or the reflective Decode path mid-stream.
func (*Reader) Bytes ¶
Bytes reads exactly n bytes and returns them as a fresh, independent slice (the data is copied out of the underlying buffer). Use Read when you already have a destination slice.
func (*Reader) Bytes32 ¶
Bytes32 reads exactly 32 bytes and returns them by value. Convenient for fields backed by solana.PublicKey or solana.Hash, which are both [32]byte.
func (*Reader) Bytes64 ¶
Bytes64 reads exactly 64 bytes and returns them by value. Convenient for fields backed by solana.Signature, which is [64]byte.
func (*Reader) Decoder ¶
Decoder returns the underlying Decoder. Mutations through it are visible to subsequent Reader calls.
func (*Reader) Done ¶
Done returns nil if all bytes were consumed exactly. If a sticky read error is pending it returns that. Otherwise, if any unread bytes remain, it returns a *TrailingBytesError reporting how many.
Use this to enforce "no extra data" at the end of a decode. Callers that want to allow trailing data should check Err() instead.
func (*Reader) Err ¶
Err returns the first error encountered, or nil. Once non-nil, all subsequent reads are no-ops and return zero values.
func (*Reader) Read ¶
Read fills out from the stream. The slice length determines how many bytes are consumed; passing pubkey[:] reads exactly 32 bytes.
func (*Reader) StrU64 ¶ added in v0.1.3
StrU64 reads a bincode string: u64 little-endian length, then UTF-8 bytes. For Borsh strings (u32 length) compose r.U32() + r.Bytes(int(n)).
type TrailingBytesError ¶
type TrailingBytesError struct {
// Remaining is the number of unread bytes at the end of the buffer.
Remaining int
}
TrailingBytesError is returned by Reader.Done when the buffer was not fully consumed. Match it via errors.As:
var te *encoding.TrailingBytesError
if errors.As(err, &te) {
// log te.Remaining, recover, etc.
}
func (*TrailingBytesError) Error ¶
func (e *TrailingBytesError) Error() string
type U128 ¶
type U128 [16]byte
U128 is a 128-bit unsigned integer stored in Rust / Solana little-endian byte order: u[0] is the least-significant byte, u[15] the most-significant. This matches the wire representation, so ReadU128 / WriteU128 are a single 16-byte memcpy on little-endian hosts.
func (U128) MarshalJSON ¶
MarshalJSON emits u as a JSON *string* decimal, so JavaScript consumers that lose precision above 2^53 still receive the exact value.
func (U128) MarshalText ¶
MarshalText implements encoding.TextMarshaler (decimal string).
func (*U128) SetBigInt ¶
SetBigInt sets u from x, which must be non-negative and fit in 128 bits. Returns an error otherwise; u is left unchanged on error.
func (*U128) UnmarshalJSON ¶
UnmarshalJSON accepts either a JSON string or a JSON number encoding a non-negative decimal U128.
func (*U128) UnmarshalText ¶
UnmarshalText parses a decimal U128 produced by MarshalText.
type U256 ¶
type U256 [32]byte
U256 is a 256-bit unsigned integer in Rust / Solana little-endian byte order (u[0] least-significant, u[31] most-significant). Used by SPL programs that expose u256 fields (oracle prices, curve parameters, some concentrated-liquidity math) and by Ethereum-style bridged state.
func (U256) MarshalJSON ¶
MarshalJSON emits u as a JSON string decimal. See U128.MarshalJSON.
func (U256) MarshalText ¶
MarshalText implements encoding.TextMarshaler (decimal string).
func (*U256) SetBigInt ¶
SetBigInt sets u from x, which must be non-negative and fit in 256 bits. Returns an error otherwise; u is left unchanged on error.
func (*U256) UnmarshalJSON ¶
UnmarshalJSON accepts either a JSON string or a JSON number encoding a non-negative decimal U256.
func (*U256) UnmarshalText ¶
UnmarshalText parses a decimal U256 produced by MarshalText.
type Unmarshaler ¶
Unmarshaler lets a type plug in hand-written decoding logic and bypass reflection entirely. Types whose addressable value satisfies this interface are dispatched directly by DecodeTo; reflection never descends into them.