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.0.0#

VestingId, AirdropId, DisperseId, EncryptedHandle, BatchInputProofToken, ClaimAuthorizationSignature, MockHandle, and (legacy alias) ManagerAddress.

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.

Constructors like asVestingId validate shape (66 chars, 0x prefix) and return the branded type. Validation is the boundary — once you're past it, the type carries the guarantee.

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