storage
Account Storage
All accounts have storage. Both resources and structures can be stored in account storage.
Paths
Objects are stored under paths. Paths consist of a domain and an identifier.
Paths start with the character /
, followed by the domain, the path separator /
,
and finally the identifier.
For example, the path /storage/test
has the domain storage
and the identifier test
.
There are only three valid domains: storage
, private
, and public
.
Objects in storage are always stored in the storage
domain.
Paths in the storage domain have type StoragePath
,
in the private domain PrivatePath
,
and in the public domain PublicPath
.
PrivatePath
and PublicPath
are subtypes of CapabilityPath
.
Both StoragePath
and CapabilityPath
are subtypes of Path
.
Path | |||
CapabilityPath | StoragePath | ||
PrivatePath | PublicPath |
Path Functions
_10fun toString(): String
Returns the string representation of the path.
_10let storagePath = /storage/path_10_10storagePath.toString() // is "/storage/path"
There are also utilities to produce paths from strings:
_10fun PublicPath(identifier: string): PublicPath?_10fun PrivatePath(identifier: string): PrivatePath?_10fun StoragePath(identifier: string): StoragePath?
Each of these functions take an identifier and produce a path of the appropriate domain:
_10let pathID = "foo"_10let path = PublicPath(identifier: pathID) // is /public/foo
Account Storage API
Account storage is accessed through the following functions of AuthAccount
.
This means that any code that has access to the authorized account has access
to all its stored objects.
_10fun save<T>(_ value: T, to: StoragePath)
Saves an object to account storage. Resources are moved into storage, and structures are copied.
T
is the type parameter for the object type.
It can be inferred from the argument's type.
If there is already an object stored under the given path, the program aborts.
The path must be a storage path, i.e., only the domain storage
is allowed.
_10fun type(at: StoragePath): Type?
Reads the type of an object from the account's storage which is stored under the given path, or nil if no object is stored under the given path.
If there is an object stored, the type of the object is returned without modifying the stored object.
The path must be a storage path, i.e., only the domain storage
is allowed
_10fun load<T>(from: StoragePath): T?
Loads an object from account storage.
If no object is stored under the given path, the function returns nil
.
If there is an object stored, the stored resource or structure is moved
out of storage and returned as an optional.
When the function returns, the storage no longer contains an object
under the given path.
T
is the type parameter for the object type.
A type argument for the parameter must be provided explicitly.
The type T
must be a supertype of the type of the loaded object.
If it is not, execution will abort with an error.
The given type does not necessarily need to be exactly the same as the type of the loaded object.
The path must be a storage path, i.e., only the domain storage
is allowed.
_10fun copy<T: AnyStruct>(from: StoragePath): T?
Returns a copy of a structure stored in account storage, without removing it from storage.
If no structure is stored under the given path, the function returns nil
.
If there is a structure stored, it is copied.
The structure stays stored in storage after the function returns.
T
is the type parameter for the structure type.
A type argument for the parameter must be provided explicitly.
The type T
must be a supertype of the type of the copied structure.
If it is not, execution will abort with an error.
The given type does not necessarily need to be exactly the same as
the type of the copied structure.
The path must be a storage path, i.e., only the domain storage
is allowed.
_80// Declare a resource named `Counter`._80//_80resource Counter {_80 pub var count: Int_80_80 pub init(count: Int) {_80 self.count = count_80 }_80}_80_80// In this example an authorized account is available through the constant `authAccount`._80_80// Create a new instance of the resource type `Counter`_80// and save it in the storage of the account._80//_80// The path `/storage/counter` is used to refer to the stored value._80// Its identifier `counter` was chosen freely and could be something else._80//_80authAccount.save(<-create Counter(count: 42), to: /storage/counter)_80_80// Run-time error: Storage already contains an object under path `/storage/counter`_80//_80authAccount.save(<-create Counter(count: 123), to: /storage/counter)_80_80// Load the `Counter` resource from storage path `/storage/counter`._80//_80// The new constant `counter` has the type `Counter?`, i.e., it is an optional,_80// and its value is the counter resource, that was saved at the beginning_80// of the example._80//_80let counter <- authAccount.load<@Counter>(from: /storage/counter)_80_80// The storage is now empty, there is no longer an object stored_80// under the path `/storage/counter`._80_80// Load the `Counter` resource again from storage path `/storage/counter`._80//_80// The new constant `counter2` has the type `Counter?` and is `nil`,_80// as nothing is stored under the path `/storage/counter` anymore,_80// because the previous load moved the counter out of storage._80//_80let counter2 <- authAccount.load<@Counter>(from: /storage/counter)_80_80// Create another new instance of the resource type `Counter`_80// and save it in the storage of the account._80//_80// The path `/storage/otherCounter` is used to refer to the stored value._80//_80authAccount.save(<-create Counter(count: 123), to: /storage/otherCounter)_80_80// Load the `Vault` resource from storage path `/storage/otherCounter`._80//_80// The new constant `vault` has the type `Vault?` and its value is `nil`,_80// as there is a resource with type `Counter` stored under the path,_80// which is not a subtype of the requested type `Vault`._80//_80let vault <- authAccount.load<@Vault>(from: /storage/otherCounter)_80_80// The storage still stores a `Counter` resource under the path `/storage/otherCounter`._80_80// Save the string "Hello, World" in storage_80// under the path `/storage/helloWorldMessage`._80_80authAccount.save("Hello, world!", to: /storage/helloWorldMessage)_80_80// Copy the stored message from storage._80//_80// After the copy, the storage still stores the string under the path._80// Unlike `load`, `copy` does not remove the object from storage._80//_80let message = authAccount.copy<String>(from: /storage/helloWorldMessage)_80_80// Create a new instance of the resource type `Vault`_80// and save it in the storage of the account._80//_80authAccount.save(<-createEmptyVault(), to: /storage/vault)_80_80// Invalid: Cannot copy a resource, as this would allow arbitrary duplication._80//_80let vault <- authAccount.copy<@Vault>(from: /storage/vault)
As it is convenient to work with objects in storage
without having to move them out of storage,
as it is necessary for resources,
it is also possible to create references to objects in storage:
This is possible using the borrow
function of an AuthAccount
:
_10fun borrow<T: &Any>(from: StoragePath): T?
Returns a reference to an object in storage without removing it from storage.
If no object is stored under the given path, the function returns nil
.
If there is an object stored, a reference is returned as an optional.
T
is the type parameter for the object type.
A type argument for the parameter must be provided explicitly.
The type argument must be a reference to any type (&Any
; Any
is the supertype of all types).
It must be possible to create the given reference type T
for the stored / borrowed object.
If it is not, execution will abort with an error.
The given type does not necessarily need to be exactly the same as the type of the borrowed object.
The path must be a storage path, i.e., only the domain storage
is allowed.
_63// Declare a resource interface named `HasCount`, that has a field `count`_63//_63resource interface HasCount {_63 count: Int_63}_63_63// Declare a resource named `Counter` that conforms to `HasCount`_63//_63resource Counter: HasCount {_63 pub var count: Int_63_63 pub init(count: Int) {_63 self.count = count_63 }_63}_63_63// In this example an authorized account is available through the constant `authAccount`._63_63// Create a new instance of the resource type `Counter`_63// and save it in the storage of the account._63//_63// The path `/storage/counter` is used to refer to the stored value._63// Its identifier `counter` was chosen freely and could be something else._63//_63authAccount.save(<-create Counter(count: 42), to: /storage/counter)_63_63// Create a reference to the object stored under path `/storage/counter`,_63// typed as `&Counter`._63//_63// `counterRef` has type `&Counter?` and is a valid reference, i.e. non-`nil`,_63// because the borrow succeeded:_63//_63// There is an object stored under path `/storage/counter`_63// and it has type `Counter`, so it can be borrowed as `&Counter`_63//_63let counterRef = authAccount.borrow<&Counter>(from: /storage/counter)_63_63counterRef?.count // is `42`_63_63// Create a reference to the object stored under path `/storage/counter`,_63// typed as `&{HasCount}`._63//_63// `hasCountRef` is non-`nil`, as there is an object stored under path `/storage/counter`,_63// and the stored value of type `Counter` conforms to the requested type `{HasCount}`:_63// the type `Counter` implements the restricted type's restriction `HasCount`_63_63let hasCountRef = authAccount.borrow<&{HasCount}>(from: /storage/counter)_63_63// Create a reference to the object stored under path `/storage/counter`,_63// typed as `&{SomethingElse}`._63//_63// `otherRef` is `nil`, as there is an object stored under path `/storage/counter`,_63// but the stored value of type `Counter` does not conform to the requested type `{Other}`:_63// the type `Counter` does not implement the restricted type's restriction `Other`_63_63let otherRef = authAccount.borrow<&{Other}>(from: /storage/counter)_63_63// Create a reference to the object stored under path `/storage/nonExistent`,_63// typed as `&{HasCount}`._63//_63// `nonExistentRef` is `nil`, as there is nothing stored under path `/storage/nonExistent`_63//_63let nonExistentRef = authAccount.borrow<&{HasCount}>(from: /storage/nonExistent)
Storage Iteration
It is possible to iterate over an account's storage using the following iteration functions:
_10fun forEachPublic(_ function: ((PublicPath, Type): Bool))_10fun forEachPrivate(_ function: ((PrivatePath, Type): Bool))_10fun forEachStored(_ function: ((StoragePath, Type): Bool))
Each of these iterates over every element in the specified domain (public, private, and storage),
applying the function argument to each.
The first argument of the function is the path of the element, and the second is its runtime type.
In the case of the private
and public
path iteration functions,
this is the runtime type of the capability linked at that path.
The Bool
return value determines whether iteration continues;
true
will proceed to the next stored element,
while false
will terminate iteration.
The specific order in which the objects are iterated over is undefined,
as is the behavior when a path is added or removed from storage.
The order of iteration is undefined. Do not rely on any particular behaviour.
Saving to or removing from storage during iteration can cause the order in which values are stored to change arbitrarily.
Continuing to iterate after such an operation will cause Cadence to panic and abort execution.
In order to avoid such errors, we recommend not modifying storage during iteration.
If you do, return false
from the iteration callback to cause iteration to end after the mutation like so:
_13account.save(1, to: /storage/foo1)_13account.save(2, to: /storage/foo2)_13account.save(3, to: /storage/foo3)_13account.save("qux", to: /storage/foo4)_13_13account.forEachStored(fun (path: StoragePath, type: Type): Bool {_13 if type == Type<String>() {_13 account.save("bar", to: /storage/foo5)_13 // returning false here ends iteration after storage is modified, preventing a panic_13 return false_13 }_13 return true_13})
The iteration will skip any broken elements in the storage.
An element could be broken due to invalid types associated with the stored value.
e.g: A value belongs to type T
of a contract with syntax/semantic errors.
Storage limit
An account's storage is limited by its storage capacity.
An account's storage used is the sum of the size of all the data that is stored in an account (in MB). An account's storage capacity is a value that is calculated from the amount of FLOW that is stored in the account's main FLOW token vault.
At the end of every transaction, the storage used is compared to the storage capacity. For all accounts involved in the transaction, if the account's storage used is greater than its storage capacity, the transaction will fail.
An account's storage used and storage capacity can be checked using the storageUsed
and storageCapacity
fields.
The fields represent current values of storage which means this would be true:
_10let storageUsedBefore = authAccount.storageUsed_10authAccount.save(<-create Counter(count: 123), to: /storage/counter)_10let storageUsedAfter = authAccount.storageUsed_10_10let storageUsedChanged = storageUsedBefore != storageUsedAfter // is true