API Reference

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>
) → Boolean

pure

#

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>
) → Boolean

pure

#

Returns whether keyOrAddress is equal to other. Assumes that a ZswapCoinPublicKey and a ContractAddress can never be equal

isContractAddress(
  keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>
) → Boolean

pure

#

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 secretKey in 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>).

k=6, rows=55

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.

k=11, rows=1348

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.

k=12, rows=2503

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.

k=6, rows=54