Capabilities
Cadence supports capability-based security through the object-capability model.
A capability in Cadence is a value that represents the right to access an object and perform certain operations on it. A capability specifies what can be accessed, and how it can be accessed.
Capabilities are unforgeable, transferable, and revocable.
Capabilities can be storage capabilities or account capabilities:
- Storage capabilities grant access to objects in account storage via paths.
- Account capabilities grant access to accounts.
Capabilities can be borrowed to get a reference to the stored object or the account it refers to.
Capabilities have the type Capability<T: &Any>
. The type parameter specifies the kind of reference that can be obtained when borrowing the capability. The type specifies the associated set of access rights through entitlements: the reference type of the capability can be authorized, which grants the owner of the capability the ability to access the fields and functions of the target that require the given entitlements.
For example, a capability that has type Capability<auth(SaveValue) &Account>
grants access to an account, and allows saving a value into the account.
Each capability has an ID, and the ID is unique per account/address.
Capabilities are created and managed through capability controllers.
Capabilities are structs, so they are copyable. They can be used (i.e., borrowed) arbitrarily many times, as long as the target capability controller has not been deleted.
Capability
General syntax:
_32access(all)_32struct Capability<T: &Any> {_32 _32 /// The address of the account that the capability targets:_32 access(all)_32 let address: Address_32_32 /// The ID of the capability:_32 access(all)_32 let id: UInt64_32_32 /// Returns a reference to the targeted object._32 ///_32 /// If the capability is revoked, the function returns nil._32 ///_32 /// If the capability targets an object in account storage,_32 /// and no object is stored at the target storage path,_32 /// the function returns nil._32 ///_32 /// If the targeted object cannot be borrowed using the given type,_32 /// the function panics._32 ///_32 access(all)_32 view fun borrow(): T?_32_32 /// Returns true if the capability currently targets an object_32 /// that satisfies the given type (i.e., could be borrowed using _32 /// the given type)._32 ///_32 access(all)_32 view fun check(): Bool_32}
Capabilities in accounts
An account exposes its capabilities through the capabilities
field, which has the type Account.Capabilities
.
_52access(all)_52struct Capabilities {_52_52 /// The storage capabilities of the account:_52 access(mapping CapabilitiesMapping)_52 let storage: Account.StorageCapabilities_52_52 /// The account capabilities of the account:_52 access(mapping CapabilitiesMapping)_52 let account: Account.AccountCapabilities_52_52 /// Returns the capability at the given public path._52 /// If the capability does not exist,_52 /// or if the given type is not a supertype of the capability's _52 /// borrow type, returns an "invalid" capability with ID 0 that_52 /// will always fail to `check` or `borrow`:_52 access(all)_52 view fun get<T: &Any>(_ path: PublicPath): Capability<T>_52_52 /// Borrows the capability at the given public path._52 /// Returns nil if the capability does not exist, or cannot be_52 /// borrowed using the given type. The function is equivalent_52 /// to `get(path).borrow()`:_52 access(all)_52 view fun borrow<T: &Any>(_ path: PublicPath): T?_52_52 /// Returns true if a capability exists at the given public path:_52 access(all)_52 view fun exists(_ path: PublicPath): Bool_52_52 /// Publish the capability at the given public path._52 ///_52 /// If there is already a capability published under the given path,_52 /// the program aborts._52 ///_52 /// The path must be a public path (i.e., only the domain `public`_52 /// is allowed):_52 access(Capabilities | PublishCapability)_52 fun publish(_ capability: Capability, at: PublicPath)_52_52 /// Unpublish the capability published at the given path._52 ///_52 /// Returns the capability if one was published at the path._52 /// Returns nil if no capability was published at the path:_52 access(Capabilities | UnpublishCapability)_52 fun unpublish(_ path: PublicPath): Capability?_52}_52_52entitlement Capabilities_52_52entitlement PublishCapability_52entitlement UnpublishCapability
Checking the existence of public capabilities
The function capabilities.check
determines if a public capability was published at the given path before:
_10access(all)_10view fun exists(_ path: PublicPath): Bool
If the account has a capability published under the given path, the function returns true; otherwise, it returns false.
Getting public capabilities with .get()
The function capabilities.get
obtains a public capability that was published before:
_10access(all)_10view fun get<T: &Any>(_ path: PublicPath): Capability<T>
If the account has a capability with the given type published under the given path, the function returns it.
If the account has no capability published under the given path, or if the given type is not a supertype of the capability's borrow type, the function returns an "invalid" capability with ID 0 that will always fail to check
or borrow
.
Borrowing public capabilities with .borrow()
The convenience function capabilities.borrow
obtains and borrows a public capability that was published before, in one step:
_10access(all)_10view fun borrow<T: &Any>(_ path: PublicPath): T?
If the account has a capability with the given type published under the given path, the function borrows the capability and returns the resulting reference as an optional.
If the account has no capability published under the given path, or the requested type, via the type parameter T
, and does not match the published capability, the function returns nil
.
Managing capabilities
Capabilities can be storage capabilities or account capabilities:
- Storage capabilities grant access to objects in account storage via paths. An account allows the management of storage capabilities through the
capabilities.storage
field, which has the typeAccount.StorageCapabilities
. - Account capabilities grant access to accounts. An account allows the management of account capabilities through the
capabilities.account
field, which has the typeAccount.AccountCapabilities
.
A capability, and all its copies, is managed by a capability controller:
- Storage capabilities are controlled by storage capability controllers. Storage capability controllers have the type
StorageCapabilityController
. - Account capabilities are controlled by account capability controllers. Account capability controllers have the type
AccountCapabilityController
.
Account.StorageCapabilities
and Account.AccountCapabilities
_96access(all)_96struct StorageCapabilities {_96_96 /// Issue/create a new storage capability:_96 access(Capabilities | StorageCapabilities | IssueStorageCapabilityController)_96 fun issue<T: &Any>(_ path: StoragePath): Capability<T>_96_96 /// Issue/create a new storage capability:_96 access(Capabilities | StorageCapabilities | IssueStorageCapabilityController)_96 fun issueWithType(_ path: StoragePath, type: Type): Capability_96_96 /// Get the storage capability controller for the capability with the _96 /// specified ID._96 ///_96 /// Returns nil if the ID does not reference an existing storage_96 /// capability:_96 access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_96 view fun getController(byCapabilityID: UInt64): &StorageCapabilityController?_96_96 /// Get all storage capability controllers for capabilities that target_96 /// this storage path:_96 access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_96 view fun getControllers(forPath: StoragePath): [&StorageCapabilityController]_96_96 /// Iterate over all storage capability controllers for capabilities_96 /// that target this storage path, passing a reference to each_96 /// controller to the provided callback function._96 ///_96 /// Iteration is stopped early if the callback function returns `false`._96 ///_96 /// If a new storage capability controller is issued for the path,_96 /// an existing storage capability controller for the path is deleted,_96 /// or a storage capability controller is retargeted from or to the_96 /// path, then the callback must stop iteration by returning false._96 /// Otherwise, iteration aborts:_96 access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_96 fun forEachController(_96 forPath: StoragePath,_96 _ function: fun(&StorageCapabilityController): Bool_96 )_96}_96_96access(all)_96struct AccountCapabilities {_96_96 /// Issue/create a new account capability:_96 access(Capabilities | AccountCapabilities | IssueAccountCapabilityController)_96 fun issue<T: &Account>(): Capability<T>_96_96 /// Issue/create a new account capability:_96 access(Capabilities | AccountCapabilities | IssueAccountCapabilityController)_96 fun issueWithType(_ type: Type): Capability_96_96 /// Get capability controller for capability with the specified ID._96 ///_96 /// Returns nil if the ID does not reference an existing account_96 /// capability:_96 access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_96 view fun getController(byCapabilityID: UInt64): &AccountCapabilityController?_96_96 /// Get all capability controllers for all account capabilities:_96 access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_96 view fun getControllers(): [&AccountCapabilityController]_96_96 /// Iterate over all account capability controllers for all account_96 /// capabilities, passing a reference to each controller to the provided_96 /// callback function._96 ///_96 /// Iteration is stopped early if the callback function returns `false`._96 ///_96 /// If a new account capability controller is issued for the account,_96 /// or an existing account capability controller for the account is_96 /// deleted, then the callback must stop iteration by returning false._96 /// Otherwise, iteration aborts:_96 access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_96 fun forEachController(_ function: fun(&AccountCapabilityController): Bool)_96}_96_96entitlement StorageCapabilities_96entitlement AccountCapabilities_96_96entitlement GetStorageCapabilityController_96entitlement IssueStorageCapabilityController_96_96entitlement GetAccountCapabilityController_96entitlement IssueAccountCapabilityController_96_96entitlement mapping CapabilitiesMapping {_96 include Identity_96_96 StorageCapabilities -> GetStorageCapabilityController_96 StorageCapabilities -> IssueStorageCapabilityController_96_96 AccountCapabilities -> GetAccountCapabilityController_96 AccountCapabilities -> IssueAccountCapabilityController_96}
AccountCapabilityController
and StorageCapabilityController
_43access(all)_43struct AccountCapabilityController {_43_43 /// The capability that is controlled by this controller:_43 access(all)_43 let capability: Capability_43_43 /// An arbitrary "tag" for the controller._43 /// For example, it could be used to describe the purpose of_43 /// the capability._43 /// Empty by default:_43 access(all)_43 var tag: String_43_43 /// Updates this controller's tag to the provided string:_43 access(all)_43 fun setTag(_ tag: String)_43_43 /// The type of the controlled capability (i.e., the T_43 /// in `Capability<T>`):_43 access(all)_43 let borrowType: Type_43_43 /// The identifier of the controlled capability._43 /// All copies of a capability have the same ID:_43 access(all)_43 let capabilityID: UInt64_43_43 /// Delete this capability controller,_43 /// and disable the controlled capability and its copies._43 ///_43 /// The controller will be deleted from storage,_43 /// but the controlled capability and its copies remain._43 ///_43 /// Once this function returns, the controller is no longer usable,_43 /// all further operations on the controller will panic._43 ///_43 /// Borrowing from the controlled capability or its copies will_43 /// return nil:_43 ///_43 access(all)_43 fun delete()_43}
_52access(all)_52struct StorageCapabilityController {_52_52 /// The capability that is controlled by this controller:_52 access(all)_52 let capability: Capability_52_52 /// An arbitrary "tag" for the controller._52 /// For example, it could be used to describe the purpose of_52 /// the capability._52 /// Empty by default:_52 access(all)_52 var tag: String_52_52 /// Updates this controller's tag to the provided string:_52 access(all)_52 fun setTag(_ tag: String)_52_52 /// The type of the controlled capability (i.e., the T_52 /// in `Capability<T>`):_52 access(all)_52 let borrowType: Type_52_52 /// The identifier of the controlled capability._52 /// All copies of a capability have the same ID._52 access(all)_52 let capabilityID: UInt64_52_52 /// Delete this capability controller,_52 /// and disable the controlled capability and its copies._52 ///_52 /// The controller will be deleted from storage,_52 /// but the controlled capability and its copies remain._52 ///_52 /// Once this function returns, the controller is no longer usable,_52 /// all further operations on the controller will panic._52 ///_52 /// Borrowing from the controlled capability or its copies_52 /// will return nil:_52 ///_52 access(all)_52 fun delete()_52_52 /// Returns the targeted storage path of the controlled capability:_52 access(all)_52 fun target(): StoragePath_52_52 /// Retarget the controlled capability to the given storage path._52 /// The path may be different or the same as the current path:_52 access(all)_52 fun retarget(_ target: StoragePath)_52}
Issuing capabilities
Capabilities are created by issuing them in the target account.
Issuing storage capabilities
The capabilities.storage.issue
function issues a new storage capability that targets the given storage path and can be borrowed with the given type:
_10access(Capabilities | StorageCapabilities | IssueStorageCapabilityController)_10fun issue<T: &Any>(_ path: StoragePath): Capability<T>
Calling the issue
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or StorageCapabilities
entitlements (aut (Capabilities) &Account
or auth(StorageCapabilities) &Account
), or the fine-grained IssueStorageCapabilityController
entitlement (auth(IssueStorageCapabilityController) &Account
).
The path must be a storage path, and it must have the domain storage
.
For example, the following transaction issues a new storage capability, which grants the ability to withdraw from the stored vault by authorizing the capability to be borrowed with the necessary Withdraw
entitlement:
_10transaction {_10 prepare(signer: auth(IssueStorageCapabilityController) &Account) {_10 let capability = signer.capabilities.storage.issue<auth(Withdraw) &Vault>(/storage/vault)_10 // ..._10 }_10}
Issuing account capabilities
The capabilities.account.issue
function issues a new account capability that targets the account and can be borrowed with the given type:
_10access(Capabilities | AccountCapabilities | IssueAccountCapabilityController)_10fun issue<T: &Account>(): Capability<T>
Calling the issue
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or AccountCapabilities
entitlements (auth(Capabilities) &Account
or auth(AccountCapabilities) &Account
), or the fine-grained IssueAccountCapabilityController
entitlement (auth(IssueAccountCapabilityController) &Account
).
For example, the following transaction issues a new account capability, which grants the ability to save objects into the account by authorizing the capability to be borrowed with the necessary SaveValue
entitlement:
_10transaction {_10 prepare(signer: auth(IssueAccountCapabilityController) &Account) {_10 let capability = signer.capabilities.account.issue<auth(SaveValue) &Account>()_10 // ..._10 }_10}
Publishing capabilities
Capabilities can be made available publicly by publishing them.
The capabilities.publish
function publishes a capability under a given public path:
_10access(Capabilities | PublishCapability)_10fun publish(_ capability: Capability, at: PublicPath)
Calling the publish
function requires access to an account via a reference that is authorized with the coarse-grained Capabilities
entitlement (auth(Capabilities) Account
), or the fine-grained PublishCapability
entitlement (auth(PublishCapability) &Account
).
For example, the following transaction issues a new storage capability and then publishes it under the path /public/vault
, allowing anyone to access and borrow the capability and gain access to the stored vault. Note that the reference type is unauthorized, so when the capability is borrowed, only publicly accessible (access(all)
) fields and functions of the object can be accessed:
_10transaction {_10 prepare(signer: auth(Capabilities) &Account) {_10 let capability = signer.capabilities.storage.issue<&Vault>(/storage/vault)_10 signer.capabilities.publish(capability, at: /public/vault)_10 }_10}
Unpublishing capabilities
The capabilities.unpublish
function unpublishes a capability from a given public path:
_10access(Capabilities | UnpublishCapability)_10fun unpublish(_ path: PublicPath): Capability?
Calling the unpublish
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
entitlement (auth(Capabilities) Account
), or the fine-grained UnpublishCapability
entitlement (auth(UnpublishCapability) &Account
).
If there is a capability published under the path, the function removes it from the path and returns it. If there is no capability published under the path, the function returns nil
.
For example, the following transaction unpublishes a capability that was previously published under the path /public/vault
:
_10transaction {_10 prepare(signer: auth(Capabilities) &Account) {_10 signer.capabilities.unpublish(/public/vault)_10 }_10}
Tagging capabilities
Capabilities can be associated with a tag, which is an arbitrary string. The tag can be used for various purposes, such as recording the purpose of the capability. It is empty by default and is stored in the capability controller.
Both storage capability controllers (StorageCapabilityController
) and account capability controllers (AccountCapabilityController
) have a tag
field and a setTag
function, which can be used to get and set the tag:
_10access(all)_10var tag: String_10_10access(all)_10fun setTag(_ tag: String)
Retargeting storage capabilities
Storage capabilities (StorageCapabilityController
) can be retargeted to another storage path after they have been issued.
The target
function returns the storage path of the controlled capability, and the retarget
function sets a new storage path:
_10access(all)_10fun target(): StoragePath_10_10access(all)_10fun retarget(_ target: StoragePath)
Revoking capabilities
A capability and all of its copies can be revoked by deleting the capability's controller.
The delete
function deletes a controller (StorageCapabilityController
or AccountCapabilityController
):
_10access(all)_10fun delete()
Getting capability controllers
The capability management types StorageCapabilities
and AccountCapabilities
allow obtaining the controller for a capability, as well as iterating over all existing controllers.
Getting a storage capability controller
The capabilities.storage.getController
function gets the storage capability controller for the capability with the given capability ID:
_10access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_10view fun getController(byCapabilityID: UInt64): &StorageCapabilityController?
Calling the getController
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or StorageCapabilities
entitlements (auth(Capabilities) &Account
or auth(StorageCapabilities) &Account
), or the fine-grained GetStorageCapabilityController
entitlement (auth(GetStorageCapabilityController) &Account
).
If a storage capability controller for the capability with the given ID exists, the function returns a reference to it, as an optional. If there is no storage capability controller with the given capability ID, the function returns nil
.
Getting an account capability controller
The capabilities.account.getController
function gets the account capability controller for the capability with the given capability ID:
_10access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_10view fun getController(byCapabilityID: UInt64): &AccountCapabilityController?
Calling the getController
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or AccountCapabilities
entitlements (auth(Capabilities) &Account
or auth(AccountCapabilities) &Account
), or the fine-grained GetAccountCapabilityController
entitlement (auth(GetAccountCapabilityController) &Account
).
If an account capability controller for the capability with the given ID exists, the function returns a reference to it, as an optional. If there is no account capability controller with the given capability ID, the function returns nil
.
Iterating over storage capability controllers
The functions getControllers
and forEachController
allow iterating over all storage capability controllers of a storage path:
_10access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_10view fun getControllers(forPath: StoragePath): [&StorageCapabilityController]_10_10access(Capabilities | StorageCapabilities | GetStorageCapabilityController)_10fun forEachController(_10 forPath: StoragePath,_10 _ function: fun(&StorageCapabilityController): Bool_10)
Calling the getControllers
and forEachController
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or StorageCapabilities
entitlements (auth(Capabilities) &Account
or auth(StorageCapabilities) &Account
), or the fine-grained GetStorageCapabilityController
entitlement (auth(GetStorageCapabilityController) &Account
).
The getControllers
function returns a new array of references to all storage capability controllers.
The forEachController
function calls the given callback function for each storage capability controller and passes a reference to the function. Iteration stops when the callback function returns false
.
Iterating over account capability controllers
The functions getControllers
and forEachController
allow iterating over all account capability controllers of the account:
_10access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_10view fun getControllers(): [&AccountCapabilityController]_10_10access(Capabilities | AccountCapabilities | GetAccountCapabilityController)_10fun forEachController(_ function: fun(&AccountCapabilityController): Bool)
Calling the getControllers
and forEachController
function requires access to an account via a reference, which is authorized with the coarse-grained Capabilities
or AccountCapabilities
entitlements (auth(Capabilities) &Account
or auth(AccountCapabilities) &Account
), or the fine-grained GetAccountCapabilityController
entitlement (auth(GetAccountCapabilityController) &Account
).
The getControllers
function returns a new array of references to all account capability controllers.
The forEachController
function calls the given callback function for each account capability controller and passes a reference to the function. Iteration stops when the callback function returns false
.
Examples
Entitlement Increment
-
Declare a resource named
Counter
that has a fieldcount
and a functionincrement
, which requires theIncrement
entitlement:_16access(all)_16resource Counter {_16_16access(all)_16var count: UInt_16_16access(all)_16init(count: UInt) {_16self.count = count_16}_16_16access(Increment)_16fun increment(by amount: UInt) {_16self.count = self.count + amount_16}_16}In this example, an account reference is available through the constant
account
, which has the typeauth(Storage, Capabilities) &Account
(i.e., the reference is authorized to perform storage and capability operations). -
Create a new instance of the resource type
Counter
and save it in the storage of the account. The path/storage/counter
is used to refer to the stored value. Its identifiercounter
was chosen freely and could be something else:_10account.storage.save(_10<-create Counter(count: 42),_10to: /storage/counter_10) -
Issue a new storage capability that allows access to the stored counter resource:
_10let capability = account.capabilities.storage.issue<&Counter>(/storage/counter) -
Publish the capability under the path
/public/counter
, so that anyone can access the counter resource. Its identifiercounter
was chosen freely and could be something else:_10account.capabilities.publish(capability, at: /public/counter)
Imagine that the next example is in a different context, such as a new script or transaction:
-
Get a reference to the account that stores the counter:
_10let account = getAccount(0x1) -
Borrow the capability for the counter that is made publicly accessible through the path
/public/counter
. Theborrow
call returns an optional reference&Counter?
. The borrow succeeds, and the result is notnil
; it is a valid reference because:- The path
/public/counter
stores a capability. - The capability allows it to be borrowed as
&Counter
, as it has the typeCapability<&Counter>
. - The target of the storage capability, the path
/storage/counter
, stores an object and it has a type that is a subtype of the borrowed type (type equality is also considered a subtype relationship).
- The path
-
Force-unwrap the optional reference. After the call, the declared constant
counterRef
has type&Counter
:_10let counterRef = account.capabilities.borrow<&Counter>(/public/counter)! -
Read the field
count
of theCounter
. The field can be accessed because it has the access modifieraccess(all)
.Even though it is a variable field (
var
), it cannot be assigned to from outside of the object._10counterRef.count // is `42`_10_10// Invalid: The `increment` function is not accessible for the_10// reference, because the reference has the type `&Counter`,_10// which does not authorize the entitlement `Increment`,_10// which is required by the `increment` function_10// (it has the access modifier ` access(Increment)`)._10_10counterRef.increment(by: 5) -
Attempt to borrow the capability again for the counter, but use the type
auth(Increment) &Counter
to re-attempt the call toincrement
.Getting the capability fails, because the capability was issued using the type
&Counter
. After the call,counterRef2
isnil
._10let counterRef2 = account.capabilities.borrow<auth(Increment) &Counter>(/public/counter)