Skip to main content
Version: Current

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
CapabilityPathStoragePath
PrivatePathPublicPath

Path Functions


_10
fun toString(): String

Returns the string representation of the path.


_10
let storagePath = /storage/path
_10
_10
storagePath.toString() // is "/storage/path"

There are also utilities to produce paths from strings:


_10
fun PublicPath(identifier: string): PublicPath?
_10
fun PrivatePath(identifier: string): PrivatePath?
_10
fun StoragePath(identifier: string): StoragePath?

Each of these functions take an identifier and produce a path of the appropriate domain:


_10
let pathID = "foo"
_10
let 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.


_10
fun 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.


_10
fun 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


_10
fun 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.


_10
fun 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
//
_80
resource 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
//
_80
authAccount.save(<-create Counter(count: 42), to: /storage/counter)
_80
_80
// Run-time error: Storage already contains an object under path `/storage/counter`
_80
//
_80
authAccount.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
//
_80
let 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
//
_80
let 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
//
_80
authAccount.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
//
_80
let 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
_80
authAccount.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
//
_80
let 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
//
_80
authAccount.save(<-createEmptyVault(), to: /storage/vault)
_80
_80
// Invalid: Cannot copy a resource, as this would allow arbitrary duplication.
_80
//
_80
let 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:


_10
fun 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
//
_63
resource interface HasCount {
_63
count: Int
_63
}
_63
_63
// Declare a resource named `Counter` that conforms to `HasCount`
_63
//
_63
resource 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
//
_63
authAccount.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
//
_63
let counterRef = authAccount.borrow<&Counter>(from: /storage/counter)
_63
_63
counterRef?.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
_63
let 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
_63
let 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
//
_63
let nonExistentRef = authAccount.borrow<&{HasCount}>(from: /storage/nonExistent)

Storage Iteration

It is possible to iterate over an account's storage using the following iteration functions:


_10
fun forEachPublic(_ function: ((PublicPath, Type): Bool))
_10
fun forEachPrivate(_ function: ((PrivatePath, Type): Bool))
_10
fun 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.

warning

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:


_13
account.save(1, to: /storage/foo1)
_13
account.save(2, to: /storage/foo2)
_13
account.save(3, to: /storage/foo3)
_13
account.save("qux", to: /storage/foo4)
_13
_13
account.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
})

info

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:


_10
let storageUsedBefore = authAccount.storageUsed
_10
authAccount.save(<-create Counter(count: 123), to: /storage/counter)
_10
let storageUsedAfter = authAccount.storageUsed
_10
_10
let storageUsedChanged = storageUsedBefore != storageUsedAfter // is true