Concept · Branded primitives

Compile-time guarantees on 32-byte strings

Tx hashes, vesting IDs, airdrop IDs, encrypted handles all look like Hex strings. The SDK brands them so the compiler refuses to swap one for another, without paying any runtime cost.

The eight branded types in v1.1.1#

VestingId, AirdropId, DisperseId, EncryptedHandle, ExternalInputProof, Signature, TxHash, and Role.

Each is a structural type with a unique brand symbol — zero-overhead at runtime, full type-safety at compile time. Pattern Z in the SDK's codebase audit.

How to make one#

components/brand.ts
ts
import { asVestingId, type VestingId } from "@tokenops/sdk";
import type { Hex } from "viem";

// "0x12...ab" as Hex
const rawHex: Hex = "0x12abc...";

// At the validation boundary — convert once, type-check forever after.
const vestingId: VestingId = asVestingId(rawHex);

// Compile error: VestingId is not assignable to AirdropId
manager.revoke({ airdropId: vestingId });

// The contract sees a plain Hex; the brand is compile-time only.

The as* constructors are zero-cost compile-time casts — the caller is responsible for prior validation (e.g. viem's isHex). The brand is an annotation, not a runtime guard: asVestingId is defined as (h: Hex): VestingId => h and performs no length or prefix check.

Receiving values from outside the SDK#

Form inputs, URL params, JSON RPC responses — all of these arrive as plain strings. Call the as* constructor at the entry point. After that, the rest of the codebase trusts the type.

See also