API Reference

Ownable API

This module provides the full Ownable module API.

For an overview of the module, read the Ownable guide.

The page covers the following modules:

  • Ownable: a single owner identified by a public account identifier.
  • ZOwnablePK: a single owner identified by a shielded commitment.
import "./node_modules/@openzeppelin/compact-contracts/access/Ownable";

Ledger

_isInitialized: Boolean

ledger

#

Initialization flag, tracked per-module to avoid the compiler's shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the Initializable module for rationale.

_owner: Either<Bytes<32>, ContractAddress>

ledger

#

The current owner, canonicalized on write. A left holds an account identifier, stored as supplied and never checked against the secret key it should derive from; a right holds a contract address. The zero value means ownership has been renounced.

Witnesses

Witness implementations are authored by the consuming DApp. The TypeScript witnesses in the repository are test-only and are not published with the package.

wit_OwnableSK() → Bytes<32>

witness

#

Returns the caller's secret key used in deriving the account identifier.

The same key produces the same account identifier across all contracts. Users who desire cross-contract unlinkability should use different keys per contract.

Circuits

initialize(initialOwner: Either<Bytes<32>, ContractAddress>) → []

circuit

#

Initializes the contract by setting the initialOwner. This must be called in the contract's constructor.

Requirements:

  • Contract is not already initialized.
  • initialOwner is not a ContractAddress.
  • initialOwner is not the zero address.

owner() → Either<Bytes<32>, ContractAddress>

circuit

#

Returns the current contract owner.

Requirements:

  • Contract is initialized.
k=7, rows=74

transferOwnership(newOwner: Either<Bytes<32>, ContractAddress>) → []

circuit

#

Transfers ownership of the contract to newOwner.

Ownership transfers to contract addresses are currently disallowed until contract-to-contract interactions are supported in Compact. This restriction prevents permanently disabling access to a circuit.

Requirements:

  • Contract is initialized.
  • The caller is the current contract owner.
  • newOwner is not a ContractAddress.
  • newOwner is not the zero address.
k=13, rows=2947

_unsafeTransferOwnership(newOwner: Either<Bytes<32>, ContractAddress>) → []

circuit

#

Unsafe variant of transferOwnership.

Ownership transfers to contract addresses are considered unsafe because contract-to-contract calls are not currently supported. Ownership privileges sent to a contract address may become uncallable. Once contract-to-contract calls are supported, this circuit may be deprecated.

Requirements:

  • Contract is initialized.
  • The caller is the current contract owner.
  • newOwner is not the zero address.
k=13, rows=2944

renounceOwnership() → []

circuit

#

Leaves the contract without an owner. It will not be possible to call assertOnlyOwner circuits anymore. Can only be called by the current owner.

Requirements:

  • Contract is initialized.
  • The caller is the current contract owner.
k=13, rows=2353

assertOnlyOwner() → []

circuit

#

Throws if called by any account other than the owner. Use this to restrict access of specific circuits to the owner.

In the case of an external (non-contract) caller, the caller’s identity is derived from the wit_OwnableSK witness as persistentHash(secretKey).

Requirements:

  • Contract is initialized.
  • The caller is the current contract owner.
k=13, rows=2349

_transferOwnership(newOwner: Either<Bytes<32>, ContractAddress>) → []

circuit

#

Transfers ownership of the contract to newOwner without enforcing permission checks on the caller.

Ownership transfers to contract addresses are currently disallowed until contract-to-contract interactions are supported in Compact. This restriction prevents circuits from being inadvertently locked in contracts.

Requirements:

  • Contract is initialized.
  • newOwner is not a ContractAddress.
k=10, rows=598

_unsafeUncheckedTransferOwnership(newOwner: Either<Bytes<32>, ContractAddress>) → []

circuit

#

Unsafe variant of _transferOwnership.

Ownership transfers to contract addresses are considered unsafe because contract-to-contract calls are not currently supported. Ownership privileges sent to a contract address may become uncallable. Once contract-to-contract calls are supported, this circuit may be deprecated.

Requirements:

  • Contract is initialized.
k=10, rows=595

import "./node_modules/@openzeppelin/compact-contracts/access/ZOwnablePK";

Ledger

_isInitialized: Boolean

ledger

#

Initialization flag, tracked per-module to avoid the compiler's shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the Initializable module for rationale.

_ownerCommitment: Bytes<32>

ledger

#

Stores the current hashed commitment representing the owner. This commitment is derived from the public identifier (e.g., SHA256(pk, nonce)), the instanceSalt, the transfer counter, and a domain separator.

A commitment of default<Bytes<32>> (i.e., zero) indicates the contract is unowned.

_counter: Counter

ledger

#

Internal transfer counter used to prevent commitment reuse.

Increments by 1 on every successful ownership transfer. Combined with id and instanceSalt to compute unique owner commitments over time.

_instanceSalt: Bytes<32>

sealed ledger

#

A per-instance value provided at initialization used to namespace commitments for this contract instance.

This salt prevents commitment collisions across contracts that might otherwise use the same owner identifiers or domain parameters. It is immutable after initialization.

Witnesses

Witness implementations are authored by the consuming DApp. The TypeScript witnesses in the repository are test-only and are not published with the package.

wit_secretNonce() → Bytes<32>

witness

#

A private per-user nonce used in deriving the shielded owner identifier.

Combined with the user's public key as SHA256(pk, nonce) to produce an obfuscated, unlinkable identity commitment. Users are encouraged to rotate this value on ownership changes.

Circuits

initialize(ownerId: Bytes<32>, instanceSalt: Bytes<32>) → []

circuit

#

Initializes the contract by setting the initial owner via ownerId and storing the instanceSalt that acts as a privacy additive for preventing duplicate commitments among other contracts implementing ZOwnablePK.

The ownerId must be calculated prior to contract deployment using the SHA256 hashing algorithm. Using any other algorithm will result in a permanent loss of contract access.

Requirements:

  • Contract is not initialized.
  • ownerId is not zero.
  • instanceSalt is not zero.

owner() → Bytes<32>

circuit

#

Returns the current commitment representing the contract owner. The full commitment is: SHA256(SHA256(pk, nonce), instanceSalt, counter, domain).

Requirements:

  • Contract is initialized.
k=6, rows=48

transferOwnership(newOwnerId: Bytes<32>) → []

circuit

#

Transfers ownership to newOwnerId. newOwnerId must be precalculated and given to the current owner off chain.

Requirements:

  • Contract is initialized.
  • Caller is the current owner.
  • newOwnerId is not an empty array.
k=15, rows=17023

renounceOwnership() → []

circuit

#

Leaves the contract without an owner. It will not be possible to call assertOnlyOwner circuits anymore. Can only be called by the current owner.

Requirements:

  • Contract is initialized.
  • Caller is the current owner.
k=14, rows=10579

assertOnlyOwner() → []

circuit

#

Throws if called by any account whose id hash SHA256(pk, nonce) does not match the stored owner commitment. Use this to only allow the owner to call specific circuits.

Requirements:

  • Contract is initialized.
  • Caller's id (SHA256(pk, nonce)) when used in _computeOwnerCommitment equals the stored _ownerCommitment, thus verifying themselves as the owner.
k=14, rows=10577

_computeOwnerCommitment(id: Bytes<32>, counter: Uint<64>) → Bytes<32>

circuit

#

Computes the owner commitment from the given id and counter.

Owner ID (id)

The id is expected to be computed off-chain as: id = SHA256(pk, nonce)

  • pk: The owner's public key.
  • nonce: A secret nonce scoped to the instance, ideally rotated with each transfer.

Commitment Derivation

commitment = SHA256(id, instanceSalt, counter, domain)

  • id: See above.
  • instanceSalt: A unique per-deployment salt, stored during initialization. This prevents commitment collisions across deployments.
  • counter: Incremented with each ownership transfer, ensuring uniqueness even with repeated id values. Cast to Field then Bytes<32> for hashing.
  • domain: Domain separator "ZOwnablePK:shield:" (padded to 32 bytes) to prevent hash collisions when extending the module or using similar commitment schemes.

Requirements:

  • Contract is initialized.
k=13, rows=6544

_computeOwnerId(pk: Either<ZswapCoinPublicKey, ContractAddress>, nonce: Bytes<32>) → Bytes<32>

pure

#

Computes the unique identifier (id) of the owner from their public key and a secret nonce.

ID Derivation

id = SHA256(pk, nonce)

  • pk: The public key of the caller. This is passed explicitly to allow for off-chain derivation, testing, or scenarios where the caller is different from the subject of the computation. We recommend using an Air-Gapped Public Key.
  • nonce: A secret nonce tied to the identity. The generation strategy is left to the user, offering different security/convenience trade-offs.

The result is a 32-byte commitment that uniquely identifies the owner. This value is later used in owner commitment hashing, and acts as a privacy-preserving alternative to a raw public key.

This module allows ownership to be tied to an identity commitment derived from a public key and secret nonce. While typically used with user public keys, this mechanism may also support contract addresses as identifiers in future contract-to-contract interactions. Both are treated as 32-byte values (Bytes<32>).

Requirements:

  • pk is not a ContractAddress.

_transferOwnership(newOwnerId: Bytes<32>) → []

circuit

#

Transfers ownership to owner id newOwnerId without enforcing permission checks on the caller.

Requirements:

  • Contract is initialized.
k=13, rows=6460