Skip to content
Algorand Developer Portal

Primitive Codecs

← Back to Common Utilities

This example demonstrates how to use primitive codec types for encoding/decoding basic values in wire format (JSON or MessagePack). Topics covered:

  • Codec interface: encode(), decode(), defaultValue() methods
  • numberCodec for encoding/decoding numbers
  • bigIntCodec for encoding/decoding BigInt values
  • booleanCodec for encoding/decoding boolean values
  • stringCodec for encoding/decoding UTF-8 strings
  • bytesCodec for encoding/decoding raw bytes
  • bytesBase64Codec for base64 encoding
  • addressCodec for encoding/decoding Algorand addresses
  • Round-trip verification: decode(encode(value)) equals original
  • No LocalNet required

From the repository root:

Terminal window
cd examples
npm run example common/09-primitive-codecs.ts

View source on GitHub

09-primitive-codecs.ts
/**
* Example: Primitive Codecs
*
* This example demonstrates how to use primitive codec types for encoding/decoding
* basic values in wire format (JSON or MessagePack).
*
* Topics covered:
* - Codec interface: encode(), decode(), defaultValue() methods
* - numberCodec for encoding/decoding numbers
* - bigIntCodec for encoding/decoding BigInt values
* - booleanCodec for encoding/decoding boolean values
* - stringCodec for encoding/decoding UTF-8 strings
* - bytesCodec for encoding/decoding raw bytes
* - bytesBase64Codec for base64 encoding
* - addressCodec for encoding/decoding Algorand addresses
* - Round-trip verification: decode(encode(value)) equals original
*
* Prerequisites:
* - No LocalNet required
*/
import type { EncodingFormat } from '@algorandfoundation/algokit-utils/common';
import {
Address,
addressCodec,
arrayEqual,
bigIntCodec,
booleanCodec,
bytesBase64Codec,
bytesCodec,
numberCodec,
stringCodec,
} from '@algorandfoundation/algokit-utils/common';
import { formatHex, printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js';
// ============================================================================
// Main Example
// ============================================================================
printHeader('Primitive Codecs Example');
// ============================================================================
// Step 1: The Codec Interface
// ============================================================================
printStep(1, 'The Codec Interface');
printInfo('Codecs provide bidirectional transformation between app types and wire formats.');
printInfo('Each codec has these core methods:');
printInfo('');
printInfo(' encode(value, format) - Transform app value to wire format');
printInfo(' decode(value, format) - Transform wire value to app value');
printInfo(' defaultValue() - Get the default value for this type');
printInfo('');
printInfo('Format can be "json" or "msgpack" - some codecs behave differently per format.');
printInfo('');
// Show default values for each codec
printInfo('Default values for each primitive codec:');
printInfo(` numberCodec.defaultValue() = ${numberCodec.defaultValue()}`);
printInfo(` bigIntCodec.defaultValue() = ${bigIntCodec.defaultValue()}n`);
printInfo(` booleanCodec.defaultValue() = ${booleanCodec.defaultValue()}`);
printInfo(` stringCodec.defaultValue() = "${stringCodec.defaultValue()}" (empty string)`);
printInfo(
` bytesCodec.defaultValue() = Uint8Array(${bytesCodec.defaultValue().length}) (empty)`,
);
printInfo(
` addressCodec.defaultValue() = ${addressCodec.defaultValue().toString().slice(0, 10)}... (zero address)`,
);
printInfo('');
printSuccess('Codec interface provides consistent encode/decode methods');
// ============================================================================
// Step 2: numberCodec - Encoding/Decoding Numbers
// ============================================================================
printStep(2, 'numberCodec - Encoding/Decoding Numbers');
printInfo('numberCodec handles JavaScript number values.');
printInfo('Numbers pass through unchanged in both JSON and msgpack formats.');
printInfo('');
const testNumbers = [0, 42, -100, 3.14159, Number.MAX_SAFE_INTEGER];
for (const num of testNumbers) {
const encodedJson = numberCodec.encode(num, 'json');
const encodedMsgpack = numberCodec.encode(num, 'msgpack');
const decodedJson = numberCodec.decode(encodedJson, 'json');
const decodedMsgpack = numberCodec.decode(encodedMsgpack, 'msgpack');
printInfo(` ${num}:`);
printInfo(
` JSON: encode=${encodedJson}, decode=${decodedJson}, matches=${num === decodedJson}`,
);
printInfo(
` msgpack: encode=${encodedMsgpack}, decode=${decodedMsgpack}, matches=${num === decodedMsgpack}`,
);
}
printInfo('');
// Round-trip verification
const roundTripNum = 12345;
const rtEncoded = numberCodec.encode(roundTripNum, 'json');
const rtDecoded = numberCodec.decode(rtEncoded, 'json');
if (rtDecoded === roundTripNum) {
printSuccess(`Round-trip: decode(encode(${roundTripNum})) === ${rtDecoded}`);
}
// ============================================================================
// Step 3: bigIntCodec - Encoding/Decoding BigInt Values
// ============================================================================
printStep(3, 'bigIntCodec - Encoding/Decoding BigInt Values');
printInfo('bigIntCodec handles JavaScript BigInt values (uint64 on Algorand).');
printInfo('Essential for values exceeding Number.MAX_SAFE_INTEGER (9007199254740991).');
printInfo('');
const testBigInts: bigint[] = [0n, 100n, 9007199254740993n, 18446744073709551615n]; // 0, 100, MAX_SAFE+2, max uint64
for (const big of testBigInts) {
const encodedJson = bigIntCodec.encode(big, 'json');
const encodedMsgpack = bigIntCodec.encode(big, 'msgpack');
const decodedJson = bigIntCodec.decode(encodedJson, 'json');
const decodedMsgpack = bigIntCodec.decode(encodedMsgpack, 'msgpack');
printInfo(` ${big}n:`);
printInfo(
` JSON: encode=${encodedJson}${typeof encodedJson === 'bigint' ? 'n' : ''}, decode=${decodedJson}n, matches=${big === decodedJson}`,
);
printInfo(
` msgpack: encode=${encodedMsgpack}${typeof encodedMsgpack === 'bigint' ? 'n' : ''}, decode=${decodedMsgpack}n, matches=${big === decodedMsgpack}`,
);
}
printInfo('');
// Round-trip verification with large value
const roundTripBig = 18446744073709551615n; // max uint64
const rtBigEncoded = bigIntCodec.encode(roundTripBig, 'msgpack');
const rtBigDecoded = bigIntCodec.decode(rtBigEncoded, 'msgpack');
if (rtBigDecoded === roundTripBig) {
printSuccess(`Round-trip: decode(encode(${roundTripBig}n)) === ${rtBigDecoded}n`);
}
// ============================================================================
// Step 4: booleanCodec - Encoding/Decoding Boolean Values
// ============================================================================
printStep(4, 'booleanCodec - Encoding/Decoding Boolean Values');
printInfo('booleanCodec handles true/false values.');
printInfo('Default value is false.');
printInfo('');
const testBools = [true, false];
for (const bool of testBools) {
const encodedJson = booleanCodec.encode(bool, 'json');
const encodedMsgpack = booleanCodec.encode(bool, 'msgpack');
const decodedJson = booleanCodec.decode(encodedJson, 'json');
const decodedMsgpack = booleanCodec.decode(encodedMsgpack, 'msgpack');
printInfo(` ${bool}:`);
printInfo(
` JSON: encode=${encodedJson}, decode=${decodedJson}, matches=${bool === decodedJson}`,
);
printInfo(
` msgpack: encode=${encodedMsgpack}, decode=${decodedMsgpack}, matches=${bool === decodedMsgpack}`,
);
}
printInfo('');
// Round-trip verification
const roundTripBool = true;
const rtBoolEncoded = booleanCodec.encode(roundTripBool, 'json');
const rtBoolDecoded = booleanCodec.decode(rtBoolEncoded, 'json');
if (rtBoolDecoded === roundTripBool) {
printSuccess(`Round-trip: decode(encode(${roundTripBool})) === ${rtBoolDecoded}`);
}
// ============================================================================
// Step 5: stringCodec - Encoding/Decoding UTF-8 Strings
// ============================================================================
printStep(5, 'stringCodec - Encoding/Decoding UTF-8 Strings');
printInfo('stringCodec handles UTF-8 string values.');
printInfo('In msgpack, strings may be received as Uint8Array (normalized on decode).');
printInfo('');
const testStrings = ['', 'Hello', 'Hello, Algorand!', 'Unicode: \u00e9\u00e8\u00ea'];
for (const str of testStrings) {
const encodedJson = stringCodec.encode(str, 'json');
const encodedMsgpack = stringCodec.encode(str, 'msgpack');
const decodedJson = stringCodec.decode(encodedJson, 'json');
const decodedMsgpack = stringCodec.decode(encodedMsgpack, 'msgpack');
const displayStr = str === '' ? '(empty)' : `"${str}"`;
printInfo(` ${displayStr}:`);
printInfo(
` JSON: encode="${encodedJson}", decode="${decodedJson}", matches=${str === decodedJson}`,
);
printInfo(
` msgpack: encode="${encodedMsgpack}", decode="${decodedMsgpack}", matches=${str === decodedMsgpack}`,
);
}
printInfo('');
// Round-trip verification
const roundTripStr = 'Algorand SDK';
const rtStrEncoded = stringCodec.encode(roundTripStr, 'json');
const rtStrDecoded = stringCodec.decode(rtStrEncoded, 'json');
if (rtStrDecoded === roundTripStr) {
printSuccess(`Round-trip: decode(encode("${roundTripStr}")) === "${rtStrDecoded}"`);
}
// ============================================================================
// Step 6: bytesCodec - Encoding/Decoding Raw Bytes
// ============================================================================
printStep(6, 'bytesCodec - Encoding/Decoding Raw Bytes');
printInfo('bytesCodec handles Uint8Array values (raw binary data).');
printInfo('JSON format: encodes to base64 string');
printInfo('msgpack format: encodes as raw bytes');
printInfo('');
const testBytes = [
new Uint8Array([]),
new Uint8Array([0x01, 0x02, 0x03]),
new Uint8Array([0xde, 0xad, 0xbe, 0xef]),
new Uint8Array(32).fill(0xab), // 32-byte key simulation
];
for (const bytes of testBytes) {
const encodedJson = bytesCodec.encode(bytes, 'json');
const encodedMsgpack = bytesCodec.encode(bytes, 'msgpack');
const decodedJson = bytesCodec.decode(encodedJson, 'json');
const decodedMsgpack = bytesCodec.decode(encodedMsgpack, 'msgpack');
const displayBytes =
bytes.length === 0 ? '(empty)' : formatHex(bytes.slice(0, 8)) + (bytes.length > 8 ? '...' : '');
printInfo(` ${displayBytes} (${bytes.length} bytes):`);
printInfo(` JSON: encode="${encodedJson}" (base64)`);
printInfo(
` decode=${formatHex(decodedJson.slice(0, 8))}${decodedJson.length > 8 ? '...' : ''}, matches=${arrayEqual(bytes, decodedJson)}`,
);
printInfo(
` msgpack: encode=${formatHex((encodedMsgpack as Uint8Array).slice(0, 8))}${(encodedMsgpack as Uint8Array).length > 8 ? '...' : ''}`,
);
printInfo(
` decode=${formatHex(decodedMsgpack.slice(0, 8))}${decodedMsgpack.length > 8 ? '...' : ''}, matches=${arrayEqual(bytes, decodedMsgpack)}`,
);
}
printInfo('');
// Round-trip verification
const roundTripBytes = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]);
const rtBytesEncodedJson = bytesCodec.encode(roundTripBytes, 'json');
const rtBytesDecodedJson = bytesCodec.decode(rtBytesEncodedJson, 'json');
if (arrayEqual(rtBytesDecodedJson, roundTripBytes)) {
printSuccess(`Round-trip (JSON): decode(encode(${formatHex(roundTripBytes)})) matches original`);
}
const rtBytesEncodedMsgpack = bytesCodec.encode(roundTripBytes, 'msgpack');
const rtBytesDecodedMsgpack = bytesCodec.decode(rtBytesEncodedMsgpack, 'msgpack');
if (arrayEqual(rtBytesDecodedMsgpack, roundTripBytes)) {
printSuccess(
`Round-trip (msgpack): decode(encode(${formatHex(roundTripBytes)})) matches original`,
);
}
// ============================================================================
// Step 7: bytesBase64Codec - Base64 Encoding for Bytes
// ============================================================================
printStep(7, 'bytesBase64Codec - Base64 Encoding for Bytes');
printInfo('bytesBase64Codec always encodes to base64 (both JSON and msgpack).');
printInfo('Used for fields that are always base64 in wire format.');
printInfo('');
const testBase64Bytes = [
new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]), // "Hello" in ASCII
new Uint8Array([0xff, 0xfe, 0xfd]),
];
for (const bytes of testBase64Bytes) {
const encodedJson = bytesBase64Codec.encode(bytes, 'json');
const encodedMsgpack = bytesBase64Codec.encode(bytes, 'msgpack');
const decodedJson = bytesBase64Codec.decode(encodedJson, 'json');
const decodedMsgpack = bytesBase64Codec.decode(encodedMsgpack, 'msgpack');
printInfo(` ${formatHex(bytes)}:`);
printInfo(` JSON: encode="${encodedJson}" (base64)`);
printInfo(
` decode=${formatHex(decodedJson)}, matches=${arrayEqual(bytes, decodedJson)}`,
);
printInfo(` msgpack: encode="${encodedMsgpack}" (base64)`);
printInfo(
` decode=${formatHex(decodedMsgpack)}, matches=${arrayEqual(bytes, decodedMsgpack)}`,
);
}
printInfo('');
// Round-trip verification
const rtB64Bytes = new Uint8Array([0x01, 0x02, 0x03]);
const rtB64Encoded = bytesBase64Codec.encode(rtB64Bytes, 'json');
const rtB64Decoded = bytesBase64Codec.decode(rtB64Encoded, 'json');
if (arrayEqual(rtB64Decoded, rtB64Bytes)) {
printSuccess(
`Round-trip: decode(encode(${formatHex(rtB64Bytes)})) via base64 "${rtB64Encoded}" matches original`,
);
}
// ============================================================================
// Step 8: addressCodec - Encoding/Decoding Algorand Addresses
// ============================================================================
printStep(8, 'addressCodec - Encoding/Decoding Algorand Addresses');
printInfo('addressCodec handles Algorand Address objects.');
printInfo('JSON format: encodes to 58-character base32 string');
printInfo('msgpack format: encodes to 32-byte public key');
printInfo('');
// Create test addresses
const zeroAddr = Address.zeroAddress();
const testAddrBytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) testAddrBytes[i] = i;
const testAddr = new Address(testAddrBytes);
const testAddresses = [zeroAddr, testAddr];
for (const addr of testAddresses) {
const encodedJson = addressCodec.encode(addr, 'json');
const encodedMsgpack = addressCodec.encode(addr, 'msgpack');
const decodedJson = addressCodec.decode(encodedJson, 'json');
const decodedMsgpack = addressCodec.decode(encodedMsgpack, 'msgpack');
const displayAddr = `${addr.toString().slice(0, 12)}...`;
printInfo(` ${displayAddr}:`);
printInfo(` JSON: encode="${(encodedJson as string).slice(0, 12)}..." (base32 string)`);
printInfo(
` decode=${decodedJson.toString().slice(0, 12)}..., matches=${addr.equals(decodedJson)}`,
);
printInfo(
` msgpack: encode=${formatHex((encodedMsgpack as Uint8Array).slice(0, 8))}... (32-byte pubkey)`,
);
printInfo(
` decode=${decodedMsgpack.toString().slice(0, 12)}..., matches=${addr.equals(decodedMsgpack)}`,
);
}
printInfo('');
// Round-trip verification
const roundTripAddr = testAddr;
const rtAddrEncodedJson = addressCodec.encode(roundTripAddr, 'json');
const rtAddrDecodedJson = addressCodec.decode(rtAddrEncodedJson, 'json');
if (rtAddrDecodedJson.equals(roundTripAddr)) {
printSuccess(
`Round-trip (JSON): decode(encode(${roundTripAddr.toString().slice(0, 12)}...)) matches original`,
);
}
const rtAddrEncodedMsgpack = addressCodec.encode(roundTripAddr, 'msgpack');
const rtAddrDecodedMsgpack = addressCodec.decode(rtAddrEncodedMsgpack, 'msgpack');
if (rtAddrDecodedMsgpack.equals(roundTripAddr)) {
printSuccess(
`Round-trip (msgpack): decode(encode(${roundTripAddr.toString().slice(0, 12)}...)) matches original`,
);
}
// ============================================================================
// Step 9: Format Differences Summary
// ============================================================================
printStep(9, 'Format Differences Summary');
printInfo('Codec behavior differs by format (json vs msgpack):');
printInfo('');
printInfo(' Codec | JSON Encoding | msgpack Encoding');
printInfo(' -------------------|----------------------|-------------------');
printInfo(' numberCodec | number | number');
printInfo(' bigIntCodec | bigint | bigint');
printInfo(' booleanCodec | boolean | boolean');
printInfo(' stringCodec | string | string (or Uint8Array)');
printInfo(' bytesCodec | base64 string | Uint8Array');
printInfo(' bytesBase64Codec | base64 string | base64 string');
printInfo(' addressCodec | 58-char base32 string| 32-byte Uint8Array');
printInfo('');
printSuccess('Codecs handle format-specific transformations automatically');
// ============================================================================
// Step 10: Round-Trip Verification Summary
// ============================================================================
printStep(10, 'Round-Trip Verification Summary');
printInfo('All primitive codecs support perfect round-trip encoding:');
printInfo('');
// Aggregate all round-trip results
const roundTrips: Array<{ name: string; value: string; format: EncodingFormat; success: boolean }> =
[];
// Number
const numVal = 42;
roundTrips.push({
name: 'numberCodec',
value: String(numVal),
format: 'json',
success: numberCodec.decode(numberCodec.encode(numVal, 'json'), 'json') === numVal,
});
// BigInt
const bigVal = 9007199254740993n;
roundTrips.push({
name: 'bigIntCodec',
value: `${bigVal}n`,
format: 'msgpack',
success: bigIntCodec.decode(bigIntCodec.encode(bigVal, 'msgpack'), 'msgpack') === bigVal,
});
// Boolean
const boolVal = true;
roundTrips.push({
name: 'booleanCodec',
value: String(boolVal),
format: 'json',
success: booleanCodec.decode(booleanCodec.encode(boolVal, 'json'), 'json') === boolVal,
});
// String
const strVal = 'Algorand';
roundTrips.push({
name: 'stringCodec',
value: `"${strVal}"`,
format: 'json',
success: stringCodec.decode(stringCodec.encode(strVal, 'json'), 'json') === strVal,
});
// Bytes
const bytesVal = new Uint8Array([1, 2, 3, 4, 5]);
roundTrips.push({
name: 'bytesCodec',
value: formatHex(bytesVal),
format: 'json',
success: arrayEqual(bytesCodec.decode(bytesCodec.encode(bytesVal, 'json'), 'json'), bytesVal),
});
roundTrips.push({
name: 'bytesCodec',
value: formatHex(bytesVal),
format: 'msgpack',
success: arrayEqual(
bytesCodec.decode(bytesCodec.encode(bytesVal, 'msgpack'), 'msgpack'),
bytesVal,
),
});
// BytesBase64
const b64Val = new Uint8Array([0xff, 0xfe, 0xfd]);
roundTrips.push({
name: 'bytesBase64Codec',
value: formatHex(b64Val),
format: 'json',
success: arrayEqual(
bytesBase64Codec.decode(bytesBase64Codec.encode(b64Val, 'json'), 'json'),
b64Val,
),
});
// Address
const addrVal = testAddr;
roundTrips.push({
name: 'addressCodec',
value: `${addrVal.toString().slice(0, 12)}...`,
format: 'json',
success: addressCodec.decode(addressCodec.encode(addrVal, 'json'), 'json').equals(addrVal),
});
roundTrips.push({
name: 'addressCodec',
value: `${addrVal.toString().slice(0, 12)}...`,
format: 'msgpack',
success: addressCodec.decode(addressCodec.encode(addrVal, 'msgpack'), 'msgpack').equals(addrVal),
});
for (const rt of roundTrips) {
const status = rt.success ? 'PASS' : 'FAIL';
printInfo(` [${status}] ${rt.name} (${rt.format}): ${rt.value}`);
}
const allPassed = roundTrips.every(rt => rt.success);
if (allPassed) {
printInfo('');
printSuccess('All round-trip verifications passed!');
}
// ============================================================================
// Summary
// ============================================================================
printStep(11, 'Summary');
printInfo('Primitive codecs for Algorand wire format encoding:');
printInfo('');
printInfo(' Codec Interface:');
printInfo(' - encode(value, format) - Transform to wire format');
printInfo(' - decode(value, format) - Transform from wire format');
printInfo(' - defaultValue() - Get type default (0, false, "", etc.)');
printInfo('');
printInfo(' Available Primitive Codecs:');
printInfo(' - numberCodec - JavaScript numbers');
printInfo(' - bigIntCodec - BigInt for uint64 values');
printInfo(' - booleanCodec - true/false values');
printInfo(' - stringCodec - UTF-8 strings');
printInfo(' - bytesCodec - Raw binary data (Uint8Array)');
printInfo(' - bytesBase64Codec - Always base64 encoded bytes');
printInfo(' - addressCodec - Algorand Address objects');
printInfo('');
printInfo(' Format Support:');
printInfo(' - "json" - JSON wire format (strings for bytes/addresses)');
printInfo(' - "msgpack" - MessagePack format (binary for bytes/addresses)');
printInfo('');
printSuccess('Primitive Codecs Example completed!');