Codecs are the foundation for instruction data, account layouts, sysvar schemas, and many protocol-level payloads.
The codec layer is split into focused packages so you can keep dependencies small while still composing rich layouts.
Core concepts#
Encoder<T>#
Turns a value into bytes.
Decoder<T>#
Turns bytes into a value.
Codec<TFrom, TTo>#
Combines both directions.
Package map#
solana_kit_codecs_core: base abstractions and composition helperssolana_kit_codecs_numbers: little-endian integers and floatssolana_kit_codecs_strings: base16/base58/base64/utf8/baseX encoders-
solana_kit_codecs_data_structures: structs, tuples, arrays, maps, unions, nullable values, and more solana_kit_options: Rust-styleOption<T>modeling and codec support
Example: build a simple struct codec#
import 'package:solana_kit_codecs_data_structures/solana_kit_codecs_data_structures.dart';
import 'package:solana_kit_codecs_numbers/solana_kit_codecs_numbers.dart';
final transferLayout = getStructCodec({
'discriminator': getU8Codec(),
'amount': getU64Codec(),
});
final bytes = transferLayout.encode({
'discriminator': 2,
'amount': BigInt.from(1_000_000),
});
final decoded = transferLayout.decode(bytes);
print(decoded['amount']);
Example: choose among variants#
Typed Union Helpers#
Prefer typed union helpers when a codec has a fixed, small number of variants. They improve IDE type inference, make exhaustive matching easier, and reduce unstructured casting in downstream code.
import 'package:solana_kit_codecs_data_structures/solana_kit_codecs_data_structures.dart';
import 'package:solana_kit_codecs_numbers/solana_kit_codecs_numbers.dart';
void main() {
final codec = getUnion2Codec(
getU8Codec(),
getU32Codec(),
(bytes, offset) => bytes.length - offset > 1 ? 1 : 0,
);
final encoded = codec.encode(const Union2Variant1<int, int>(1000));
final decoded = codec.decode(encoded);
print(encoded);
print(decoded);
}
Use these helpers when your wire format has “one of a few known cases” and you want the Dart type system to preserve that fact.
Pattern-driven codec selection#
Pattern-match codecs#
Use pattern-match codecs when you need to choose a codec based on either the incoming bytes or the value being encoded.
import 'dart:typed_data';
import 'package:solana_kit_codecs_data_structures/solana_kit_codecs_data_structures.dart';
import 'package:solana_kit_codecs_numbers/solana_kit_codecs_numbers.dart';
void main() {
final codec = getPatternMatchCodec<num, int>([
((value) => value < 256, (bytes) => bytes.length == 1, getU8Codec()),
((value) => value >= 256, (bytes) => bytes.length == 2, getU16Codec()),
]);
final encoded = codec.encode(513);
final decoded = codec.decode(Uint8List.fromList(encoded));
print(decoded);
}
Reach for this when a layout cannot be described as a single fixed struct or union discriminator alone.
UTF-8 decoding behavior#
UTF-8 data integrity
getUtf8Codec()preserves embedded null characters after UTF-8 decode.Malformed UTF-8 is rejected rather than repaired or replaced, so callers can distinguish invalid bytes from valid text.
getUtf8Codec() preserves embedded null characters and rejects malformed UTF-8.
import 'package:solana_kit_codecs_strings/solana_kit_codecs_strings.dart';
void main() {
final utf8 = getUtf8Codec();
print(utf8);
}
- use
getUtf8Codec()when you need a faithful UTF-8 decoding boundary. - use
removeNullCharacters()only when null padding must be discarded explicitly.
When to use codecs#
Use codecs when you need:
- deterministic byte layouts
- testable encode/decode logic
- reuse across program clients and account decoders
- confidence that a Dart representation matches an upstream wire format
Avoid ad hoc Uint8List slicing once a layout becomes non-trivial. A codec pays for itself quickly.