Ownable

Ownable is a standalone, token-agnostic single-owner primitive for Daml. It is the analogue of OpenZeppelin's Ownable.sol and Ownable2Step.sol: exactly one owner party at a time, with an explicit transfer handshake and a renounce path.

The package is independent: it carries no role type and has no dependency on Access Control, so a project that wants only "an owner" imports only this one package.

Version 0.x, unstable, not yet public API. Interfaces may change before a 1.0 release. Source: OpenZeppelin/canton-contracts.

import OpenZeppelin.Ownable

Why transfer is always two-step in Daml

In Solidity transferOwnership(newOwner) is a single call: the old owner writes the new owner into storage. That is not possible in Daml. The owner is a signatory of the Ownership contract, and a contract cannot be created without the authorization of every signatory, so the current owner cannot unilaterally bind a new owner.

Transfer is therefore a handshake: the owner makes an OwnershipOffer, and the prospective owner accepts it. This is exactly why OpenZeppelin recommends Ownable2Step on other ecosystems, and here it is the only correct shape, not merely the safer one. It also guarantees ownership never lands on a party that has not actively agreed to hold it.

During a pending offer, ownership is suspended: the Ownership contract is archived and re-created on accept (to the new owner) or on withdraw / decline (back to the current owner). Consumers that gate on "owner exists" must treat the offer window accordingly.

Templates

Ownership

Sole ownership of a resource by owner.

FieldTypeDescription
ownerPartyThe current sole owner.

Signatory owner. Every choice is owner-controlled.

  • Ownership_OfferOwnership (returns ContractId OwnershipOffer): begin a two-step transfer to newOwner. Consuming: ownership is suspended into the returned offer until accepted, withdrawn, or declined. Offering to the current owner is rejected.
  • Ownership_RenounceOwnership (returns ()): renounce ownership, leaving the resource permanently ownerless (the renounceOwnership analogue). Irreversible.

OwnershipOffer

A pending ownership transfer awaiting newOwner's decision.

FieldTypeDescription
ownerPartyThe current owner who made the offer.
newOwnerPartyThe prospective owner who must accept.

Signatory owner, observer newOwner.

ChoiceControllerResultDescription
OwnershipOffer_AcceptnewOwnerContractId OwnershipAccept the transfer. Creates Ownership signed by newOwner, whose authorization is what makes the transfer sound.
OwnershipOffer_DeclinenewOwnerContractId OwnershipDecline; ownership returns to the offerer.
OwnershipOffer_WithdrawownerContractId OwnershipWithdraw the offer; ownership returns to the offerer.

Transferring ownership

-- current owner offers
offerCid <- exercise ownershipCid Ownership_OfferOwnership with newOwner = bob

-- prospective owner accepts, becoming the new owner
newOwnershipCid <- exercise offerCid OwnershipOffer_Accept

Errors

MessageWhen
Ownable: new owner is the current ownerAn offer named the current owner.
  • Access Control, when more than one party or action needs independent authorization.
  • Pausable, for an emergency stop.