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.
initialOwneris not a ContractAddress.initialOwneris not the zero address.
owner() → Either<Bytes<32>, ContractAddress>
circuit
#Returns the current contract owner.
Requirements:
- Contract is initialized.
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.
newOwneris not a ContractAddress.newOwneris not the zero address.
_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.
newOwneris not the zero address.
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.
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.
_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.
newOwneris not a ContractAddress.
_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.
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.
ownerIdis not zero.instanceSaltis 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.
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.
newOwnerIdis not an empty array.
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.
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.
_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 repeatedidvalues. Cast toFieldthenBytes<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.
_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:
pkis 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.