Utils
This package provides the API for all Utils modules.
For an overview of the modules, read the Utils guide.
The page covers the following modules:
- Utils: miscellaneous circuits and common utilities for Compact contract development.
- EvmAbi: Ethereum ABI word encoding, for messages that must hash identically in Compact and on the EVM.
import "./node_modules/@openzeppelin/compact-contracts/utils/Utils";Ledger
None.
Witnesses
None.
Circuits
isKeyOrAddressZero(
keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>
) → Booleanpure
#Returns whether keyOrAddress is the zero address.
Midnight's burn address is represented as left<ZswapCoinPublicKey, ContractAddress>(default<ZswapCoinPublicKey>) in Compact, so we've chosen to represent the zero address as this structure as well.
isKeyZero(key: ZswapCoinPublicKey) → Boolean
pure
#Returns whether key is the zero address.
isKeyOrAddressEqual(
keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>,
other: Either<ZswapCoinPublicKey, ContractAddress>
) → Booleanpure
#Returns whether keyOrAddress is equal to other. Assumes that a ZswapCoinPublicKey
and a ContractAddress can never be equal
isContractAddress(
keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>
) → Booleanpure
#Returns whether keyOrAddress is a ContractAddress type.
emptyString() → Opaque<"string">
pure
#A helper function that returns the empty string: "".
canonicalize<T1, T2>(value: Either<T1, T2>) → Either<T1, T2>
pure
#Zeroes out the unused side of an Either value. Prevents crafted
inputs where both left and right fields carry data from bypassing checks that
only inspect the active side.
zeroAccount() → Either<Bytes<32>, ContractAddress>
pure
#Returns the canonical zero Either<Bytes<32>, ContractAddress> value: the
left variant (Bytes<32>) set to zero. Used as the sentinel for mint/burn operations
(a zero from signals a mint, a zero to signals a burn) and for cleared approvals.
The left variant is chosen so that isTargetZero checks against default<Bytes<32>>,
and to avoid triggering contract-address guards (e.g. circuits that check !to.is_left
before delegating to an _unsafe variant). A right variant with default<ContractAddress>
would also pass isTargetZero, but the left variant keeps guards and error messages consistent.
isTargetZero(target: Either<Bytes<32>, ContractAddress>) → Boolean
pure
#Returns true if target's active branch (as indicated by is_left)
holds the zero value.
computeAccountId(secretKey: Bytes<32>) → Bytes<32>
pure
#Computes an account identifier without on-chain state, allowing a user to derive their identity commitment before submitting an operation.
OpSec: The secretKey parameter is a sensitive secret. Mishandling it can
permanently compromise the security of this system:
- Never log or persist the
secretKeyin plaintext — avoid browser devtools, application logs, analytics pipelines, or any observable side-channel. - Store offline or in secure enclaves — hardware security modules (HSMs), air-gapped devices, or encrypted vaults are strongly preferred over hot storage.
- Use cryptographically secure randomness — generate keys with
crypto.getRandomValues()or equivalent; weak or predictable keys can be brute-forced to reveal your identity. - Treat key loss as identity loss — a lost key cannot be recovered.
- Avoid calling this circuit in untrusted environments — executing this in an unverified browser extension, compromised runtime, or shared machine may expose the key to a malicious observer.
ID Derivation
accountId = persistentHash(secretKey)
The absence of a domain-separation tag is DELIBERATE. This identifier is global by design: the same key material yields the same identity in every module deriving one, so a user who wishes to carry one identity across modules can. Modules wanting a per-deployment, unlinkable identity should not use this circuit.
NOT FOR PRIVATE DERIVATION. This returns an identifier, not a secret, and the construction is untagged. A circuit deriving a PRIVATE value from a secret MUST use its own domain-separation tag.
selfAsRecipient() → Either<ZswapCoinPublicKey, ContractAddress>
circuit
#Returns the current contract's address as an
Either<ZswapCoinPublicKey, ContractAddress> for use as a
recipient in shielded send operations (deposits and receiving change).
UINT128_MAX() → Uint<128>
pure
#The maximum value representable by a Uint<128>.
TODO: Remove once a math module providing this constant is available.
import "./node_modules/@openzeppelin/compact-contracts/utils/EvmAbi";The module needs no keccak256 of its own and compiles on either ZKIR backend.
Every circuit size below is a ZKIR v3 figure.
Ledger
None.
Witnesses
None.
Circuits
uint8Word(value: Uint<8>) → Bytes<32>
pure
#Encodes a Uint<8> as an abi.encode(uint8) word: the
value in the least significant byte, zero elsewhere.
This is also how a Compact enum should reach a digest, matching Solidity,
where an enum ABI-encodes as its uint8 ordinal: uint8Word(kind as Uint<8>).
uint64Word(value: Uint<64>) → Bytes<32>
pure
#Encodes a Uint<64> as an abi.encode(uint256) word: the
value big-endian in the low 8 bytes, zero-padded on the left.
uint128Word(value: Uint<128>) → Bytes<32>
pure
#Encodes a Uint<128> as an abi.encode(uint256) word: the
value big-endian in the low 16 bytes, zero-padded on the left.
Costs roughly twice uint64Word, so prefer the narrower encoder when the value fits in 64 bits.
boolWord(value: Boolean) → Bytes<32>
pure
#Encodes a Boolean as an abi.encode(bool) word: zero
everywhere except the least significant byte, which is 1 for true.