Skip to content

Domain Types

The @unionlabs/payments/Domain module makes use of Branded Types from Effect. This improves safety when working with data that is structurally identical but semantically different.

Consider the case where a ZAsset address is required for input to a particular function:

const
const handleZAsset: (zAssetAddress: `0x${string}`) => void
handleZAsset
= (
zAssetAddress: `0x${string}`
zAssetAddress
: `0x${string}`) => {}

The 0x${string} type is permissive, and passing semantically incorrect data is feasible:

const
const usdcOnBase: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
usdcOnBase
= "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" as
type const = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
const
const handleZAsset: (addr: `0x${string}`) => void
handleZAsset
(
const usdcOnBase: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
usdcOnBase
)

We know that usdcOnBase is not a ZAsset address, but there is no static analysis protection which can prevent the mistake.

To aid in preventing this class of mistakes, we make use of branding to explicitly denote the semantic meaning of a 0x${string}.

import {
import Domain
Domain
} from "@unionlabs/payments"
const usdcOnBase =
import Domain
Domain
.
const Erc20Address: Brand<in out K extends string | symbol>.Constructor
(args: `0x${string}`) => Domain.Erc20Address

Constructs a branded type from a value of type A, throwing an error if the provided A is not valid.

Erc20Address
(
const usdcOnBase: Domain.Erc20Address
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
)
const
const handleZAsset: (zAssetAddress: Domain.ZAssetAddress) => void
handleZAsset
= (
zAssetAddress: Domain.ZAssetAddress
zAssetAddress
:
import Domain
Domain
.ZAssetAddress
type ZAssetAddress = `0x${string}` & Brand<"ZAssetAddress">
) => {}
const handleZAsset: (zAssetAddress: Domain.ZAssetAddress) => void
handleZAsset
(usdcOnBase)
Error ts(2345) ― Argument of type 'Erc20Address' is not assignable to parameter of type 'ZAssetAddress'. Type 'Erc20Address' is not assignable to type 'Brand<"ZAssetAddress">'. Types of property '[BrandTypeId]' are incompatible. Property 'ZAssetAddress' is missing in type '{ readonly Erc20Address: "Erc20Address"; }' but required in type '{ readonly ZAssetAddress: "ZAssetAddress"; }'.