Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
7a49408
start ownable
andrew-fleming Jun 26, 2025
20d258e
normalize 'add me' comments
andrew-fleming Jun 30, 2025
bb8e452
fix conflicts
andrew-fleming Jul 2, 2025
52d7c37
tidy up module, add unsafeInitialize, add tests
andrew-fleming Jul 3, 2025
48edb8b
simplify ownable, add init check, add unsafeInit, add tests
andrew-fleming Jul 4, 2025
775cfd0
add additional tests
andrew-fleming Jul 4, 2025
ae25305
fix fmt
andrew-fleming Jul 4, 2025
adcf477
start documentation in comments
andrew-fleming Jul 5, 2025
0b5963a
finish doc comments
andrew-fleming Jul 5, 2025
3b2e871
fix assert circuit
andrew-fleming Jul 5, 2025
3aa39a6
add warning to initialize
andrew-fleming Jul 5, 2025
7813c22
fix comment
andrew-fleming Jul 5, 2025
f306bed
tidy up code
andrew-fleming Jul 5, 2025
af31aaa
improve doc comments
andrew-fleming Jul 5, 2025
9d31b45
fix comment
andrew-fleming Jul 5, 2025
5496517
default collapse Ownable
andrew-fleming Jul 5, 2025
7438da3
add ownable to nav
andrew-fleming Jul 5, 2025
fd669db
add ownable API
andrew-fleming Jul 5, 2025
06af07e
start ownable page
andrew-fleming Jul 5, 2025
547f77f
remove unsafeInit
andrew-fleming Jul 5, 2025
0a92218
improve initialize doc
andrew-fleming Jul 5, 2025
02f0a7c
remove underscore from assert
andrew-fleming Jul 5, 2025
893a611
remove underscore from assert
andrew-fleming Jul 5, 2025
53a16a0
improve comment
andrew-fleming Jul 5, 2025
16e39a9
add sections for transfers, experimental, and usage for ownable
andrew-fleming Jul 5, 2025
2185b50
fix spacing
andrew-fleming Jul 5, 2025
b9501e6
improve test msg
andrew-fleming Jul 6, 2025
9bcb7f5
change midnight to compact
andrew-fleming Jul 6, 2025
0082f82
update yarn.lock
andrew-fleming Jul 6, 2025
202999e
fix fmt
andrew-fleming Jul 6, 2025
b12f3e1
fix output
andrew-fleming Jul 6, 2025
a9170bb
fix xref link
andrew-fleming Jul 6, 2025
77b1189
improve sentence
andrew-fleming Jul 6, 2025
3a58dc4
fix param in doc
andrew-fleming Jul 6, 2025
f545b29
update comments in simulator
andrew-fleming Jul 6, 2025
c12c5ba
remove unused file
andrew-fleming Jul 6, 2025
5af8dbd
fix fmt
andrew-fleming Jul 6, 2025
dc440f4
tidy up tests
andrew-fleming Jul 6, 2025
07fbcbb
Merge branch 'main' into add-ownable
andrew-fleming Jul 9, 2025
da01a1b
fix conflicts
andrew-fleming Jul 11, 2025
cece08c
fix imports
andrew-fleming Jul 11, 2025
56545a1
Merge branch 'main' into add-ownable
andrew-fleming Jul 14, 2025
40881c2
fix conflicts
andrew-fleming Jul 15, 2025
bc52c8f
Apply suggestions from code review
andrew-fleming Jul 16, 2025
5b41752
fix imports
andrew-fleming Jul 16, 2025
9bc7af2
fix requirements, improve restriction notes
andrew-fleming Jul 16, 2025
f5adc2c
remove unused return statements from sim methods
andrew-fleming Jul 16, 2025
e442f98
Merge branch 'main' into add-ownable
andrew-fleming Jul 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions contracts/ownable/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@openzeppelin-compact/ownable",
"private": true,
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"scripts": {
"compact": "compact-compiler",
"build": "compact-builder && tsc",
"test": "vitest run",
"types": "tsc -p tsconfig.json --noEmit",
"clean": "git clean -fXd"
},
"dependencies": {
"@openzeppelin-compact/compact": "workspace:^"
},
"devDependencies": {
"@types/node": "22.14.0",
"ts-node": "^10.9.2",
"typescript": "^5.2.2",
"vitest": "^3.1.3"
}
}
198 changes: 198 additions & 0 deletions contracts/ownable/src/Ownable.compact
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: MIT

pragma language_version >= 0.15.0;

/**
* @module Ownable
* @description An unshielded Ownable library.
* This modules provides a basic access control mechanism, where there is an owner
* that can be granted exclusive access to specific circuits.
* This approach is perfectly reasonable for contracts that have a single administrative user.
*
* The initial owner must be set by using the `initialize` circuit during construction.
* This can later be changed with `transferOwnership`.
*
* @notice Ownership can only be transferred to ZSwapCoinPublicKeys
* through the main transfer circuits (`transferOwnership` and `_transferOwnership`).
* In other words, ownership transfers to contract addresses are disallowed through these
* circuits.
* This is because Compact currently does not support contract-to-contract calls which means
* if a contract is granted ownership, the owner contract cannot directly call the protected
* circuit.
*
* @notice This module does offer experimental circuits that allow ownership to be granted
* to contract addresses (`_unsafeTransferOwnshiper` and `_unsafeUncheckedTransferOwnership`).
* Note that the circuit names are very explicit ("unsafe") with these experimental circuits.
* Until contract-to-contract calls are supported,
* there is no direct way for a contract to call circuits of other contracts
* or transfer ownership back to a user.
*
* @notice The unsafe circuits are planned to become deprecated once contract-to-contract calls
* are supported.
*/
module Ownable {
import CompactStandardLibrary;
import "../../node_modules/@openzeppelin-compact/utils/src/Utils" prefix Utils_;
import "../../node_modules/@openzeppelin-compact/utils/src/Initializable" prefix Initializable_;

export ledger _owner: Either<ZswapCoinPublicKey, ContractAddress>;

/**
* @description 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.
*
* @param {Either<ZswapCoinPublicKey, ContractAddress>} initialOwner - The initial owner of the contract.
* @returns {[]} Empty tuple.
*/
export circuit initialize(initialOwner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
Initializable_initialize();
assert !Utils_isKeyOrAddressZero(initialOwner) "Ownable: invalid initial owner";
_transferOwnership(initialOwner);
}

/**
* @description Returns the current contract owner.
*
* Requirements:
*
* - Contract is initialized.
*
* @returns {Either<ZswapCoinPublicKey, ContractAddress> } - The contract owner.
*/
export circuit owner(): Either<ZswapCoinPublicKey, ContractAddress> {
Initializable_assertInitialized();
return _owner;
}

/**
* @description Transfers ownership of the contract to `newOwner`.
*
* @notice 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.
* - The caller is the current contract owner.
* - `newOwner` is not a ContractAddress.
* - `newOwner` is not the zero address.
*
* @param {Either<ZswapCoinPublicKey, ContractAddress>} newOwner - The new owner.
* @returns {[]} Empty tuple.
*/
export circuit transferOwnership(newOwner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
Initializable_assertInitialized();
assert !Utils_isContractAddress(newOwner) "Ownable: unsafe ownership transfer";
_unsafeTransferOwnership(newOwner);
}

/**
* @description Unsafe variant of `transferOwnership`.
*
* @warning 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.
*
* @param {Either<ZswapCoinPublicKey, ContractAddress>} newOwner - The new owner.
* @returns {[]} Empty tuple.
*/
export circuit _unsafeTransferOwnership(newOwner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
Initializable_assertInitialized();
assertOnlyOwner();
assert !Utils_isKeyOrAddressZero(newOwner) "Ownable: invalid new owner";
_unsafeUncheckedTransferOwnership(newOwner);
}

/**
* @description Leaves the contract without an owner.
* It will not be possible to call `assertOnlyOnwer` circuits anymore.
* Can only be called by the current owner.
*
* Requirements:
*
* - Contract is initialized.
* - The caller is the current contract owner.
*
* @returns {[]} Empty tuple.
*/
export circuit renounceOwnership(): [] {
Initializable_assertInitialized();
assertOnlyOwner();
_transferOwnership(burn_address());
}

/**
* @description Throws if called by any account other than the owner.
* Use this to restrict access of specific circuits to the owner.
*
* Requirements:
*
* - Contract is initialized.
* - The caller is the current contract owner.
*
* @returns {[]} Empty tuple.
*/
export circuit assertOnlyOwner(): [] {
Initializable_assertInitialized();
const caller = own_public_key();
assert caller == _owner.left "Ownable: caller is not the owner";
}

/**
* @description Transfers ownership of the contract to `newOwner` without
* enforcing permission checks on the caller.
*
* @notice 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.
* - `newOwner` is not the zero address.
*
* @param {Either<ZswapCoinPublicKey, ContractAddress>} newOwner - The new owner.
* @returns {[]} Empty tuple.
*/
export circuit _transferOwnership(newOwner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
Initializable_assertInitialized();
assert !Utils_isContractAddress(newOwner) "Ownable: unsafe ownership transfer";
_unsafeUncheckedTransferOwnership(newOwner);
}

/**
* @description Unsafe variant of `_transferOwnership`.
*
* @warning 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.
* - `newOwner` is not the zero address.
*
* @param {Either<ZswapCoinPublicKey, ContractAddress>} newOwner - The new owner.
* @returns {[]} Empty tuple.
*/
export circuit _unsafeUncheckedTransferOwnership(newOwner: Either<ZswapCoinPublicKey, ContractAddress>): [] {
Initializable_assertInitialized();
_owner = newOwner;
}
}
Loading
Loading