Skip to main content

The Pattern

pragma language_version >= 0.17.0;

import CompactStandardLibrary;

// Store contract owner
export ledger owner: Either<ZswapCoinPublicKey, ContractAddress>;

// Initialize owner in constructor
circuit constructor(): [] {
    owner = disclose(left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey()));
}

// Owner-only function
export circuit restrictedFunction(): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner, "Only owner can call");

    // Protected logic here
}

// Get caller address
export circuit getCaller(): Either<ZswapCoinPublicKey, ContractAddress> {
    return left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
}

Owner-Only Pattern

Set Owner in Constructor

export ledger owner: Either<ZswapCoinPublicKey, ContractAddress>;

circuit constructor(): [] {
    owner = disclose(left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey()));
}
The deployer automatically becomes the owner.

Restrict Function Access

export circuit mint(
    account: Either<ZswapCoinPublicKey, ContractAddress>,
    amount: Uint<128>
): [] {
    // Only owner can mint
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner, "Only owner can mint");

    _mint(account, amount);
}
Check caller matches owner before executing.

Transfer Ownership

export circuit transferOwnership(
    newOwner: Either<ZswapCoinPublicKey, ContractAddress>
): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner, "Only owner can transfer");

    owner = disclose(newOwner);
}
Allow current owner to transfer to new owner.

Caller Identification

Get Caller Address

// In any circuit
const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
ownPublicKey() returns the public key of whoever called the circuit.

Authorize Sender

export circuit transfer(
    to: Either<ZswapCoinPublicKey, ContractAddress>,
    amount: Uint<128>
): [] {
    // Sender is always the caller
    const from = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());

    // Transfer from caller's balance
    _transfer(from, to, amount);
}
The caller is automatically authorized to spend their own tokens.

Role-Based Access

Multiple Roles

export ledger owner: Either<ZswapCoinPublicKey, ContractAddress>;
export ledger minter: Either<ZswapCoinPublicKey, ContractAddress>;
export ledger burner: Either<ZswapCoinPublicKey, ContractAddress>;

// Owner can mint and burn
export circuit mint(account: Either<ZswapCoinPublicKey, ContractAddress>, amount: Uint<128>): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner || caller == minter, "Not authorized to mint");
    _mint(account, amount);
}

export circuit burn(amount: Uint<128>): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner || caller == burner, "Not authorized to burn");
    _burn(caller, amount);
}
Different roles for different operations.

Admin Functions

export circuit setMinter(newMinter: Either<ZswapCoinPublicKey, ContractAddress>): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner, "Only owner");

    minter = disclose(newMinter);
}

export circuit setBurner(newBurner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
    const caller = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
    assert(caller == owner, "Only owner");

    burner = disclose(newBurner);
}
Owner manages role assignments.

What’s Next