Accounts

Every account can be accessed through two types, PublicAccount and AuthAccount.

PublicAccount

Public Account objects have the type PublicAccount, which represents the publicly available portion of an account.

1
struct PublicAccount {
2
3
let address: Address
4
// The FLOW balance of the default vault of this account
5
let balance: UFix64
6
// The FLOW balance of the default vault of this account that is available to be moved
7
let availableBalance: UFix64
8
// Amount of storage used by the account, in bytes
9
let storageUsed: UInt64
10
// storage capacity of the account, in bytes
11
let storageCapacity: UInt64
12
13
// Contracts deployed to the account
14
let contracts: PublicAccount.Contracts
15
16
// Keys assigned to the account
17
let keys: PublicAccount.Keys
18
19
// The public paths associated with this account
20
let publicPaths: [PublicPath]
21
22
// Storage operations
23
24
fun getCapability<T>(_ path: PublicPath): Capability<T>
25
fun getLinkTarget(_ path: CapabilityPath): Path?
26
27
// Storage iteration
28
fun forEachPublic(_ function: ((PublicPath, Type): Bool))
29
30
struct Contracts {
31
32
let names: [String]
33
34
fun get(name: String): DeployedContract?
35
}
36
37
struct Keys {
38
// Returns the key at the given index, if it exists.
39
// Revoked keys are always returned, but they have \`isRevoked\` field set to true.
40
fun get(keyIndex: Int): AccountKey?
41
42
// Iterate over all unrevoked keys in this account,
43
// passing each key in turn to the provided function.
44
// Iteration is stopped early if the function returns `false`.
45
// The order of iteration is undefined.
46
fun forEach(function: ((AccountKey): Bool)): Void
47
48
// The total number of unrevoked keys in this account.
49
let count: UInt64
50
}
51
}

Any code can get the PublicAccount for an account address using the built-in getAccount function:

1
fun getAccount(_ address: Address): PublicAccount

AuthAccount

Authorized Account object have the type AuthAccount, which represents the authorized portion of an account.

Access to an AuthAccount means having full access to its storage, public keys, and code.

Only signed transactions can get the AuthAccount for an account. For each signer of the transaction that signs as an authorizer, the corresponding AuthAccount object is passed to the prepare phase of the transaction.

1
struct AuthAccount {
2
3
let address: Address
4
// The FLOW balance of the default vault of this account
5
let balance: UFix64
6
// The FLOW balance of the default vault of this account that is available to be moved
7
let availableBalance: UFix64
8
// Amount of storage used by the account, in bytes
9
let storageUsed: UInt64
10
// storage capacity of the account, in bytes
11
let storageCapacity: UInt64
12
13
// Contracts deployed to the account
14
15
let contracts: AuthAccount.Contracts
16
17
// Keys assigned to the account
18
19
let keys: AuthAccount.Keys
20
21
// Provides a API for bootstrapping (sending and receiving) capabilities
22
23
let inbox: AuthAccount.Inbox
24
25
// All the paths associated with this account
26
let publicPaths: [PublicPath]
27
let privatePaths: [PrivatePath]
28
let storagePaths: [StoragePath]
29
30
// Key management
31
32
// Adds a public key to the account.
33
// The public key must be encoded together with their signature algorithm, hashing algorithm and weight.
34
// This method is currently deprecated and is available only for the backward compatibility.
35
// `keys.add` method can be use instead.
36
fun addPublicKey(_ publicKey: [UInt8])
37
38
// Revokes the key at the given index.
39
// This method is currently deprecated and is available only for the backward compatibility.
40
// `keys.revoke` method can be use instead.
41
fun removePublicKey(_ index: Int)
42
43
// Account storage API (see the section below for documentation)
44
45
fun save<T>(_ value: T, to: StoragePath)
46
fun type(at path: StoragePath): Type?
47
fun load<T>(from: StoragePath): T?
48
fun copy<T: AnyStruct>(from: StoragePath): T?
49
50
fun borrow<T: &Any>(from: StoragePath): T?
51
52
fun link<T: &Any>(_ newCapabilityPath: CapabilityPath, target: Path): Capability<T>?
53
fun getCapability<T>(_ path: CapabilityPath): Capability<T>
54
fun getLinkTarget(_ path: CapabilityPath): Path?
55
fun unlink(_ path: CapabilityPath)
56
57
// Storage iteration
58
fun forEachPublic(_ function: ((PublicPath, Type): Bool))
59
fun forEachPrivate(_ function: ((PrivatePath, Type): Bool))
60
fun forEachStored(_ function: ((StoragePath, Type): Bool))
61
62
struct Contracts {
63
64
// The names of each contract deployed to the account
65
let names: [String]
66
67
fun add(
68
name: String,
69
code: [UInt8],
70
... contractInitializerArguments
71
): DeployedContract
72
73
fun update__experimental(name: String, code: [UInt8]): DeployedContract
74
75
fun get(name: String): DeployedContract?
76
77
fun remove(name: String): DeployedContract?
78
}
79
80
struct Keys {
81
// Adds a new key with the given hashing algorithm and a weight, and returns the added key.
82
fun add(
83
publicKey: PublicKey,
84
hashAlgorithm: HashAlgorithm,
85
weight: UFix64
86
): AccountKey
87
88
// Returns the key at the given index, if it exists, or nil otherwise.
89
// Revoked keys are always returned, but they have `isRevoked` field set to true.
90
fun get(keyIndex: Int): AccountKey?
91
92
// Marks the key at the given index revoked, but does not delete it.
93
// Returns the revoked key if it exists, or nil otherwise.
94
fun revoke(keyIndex: Int): AccountKey?
95
96
// Iterate over all unrevoked keys in this account,
97
// passing each key in turn to the provided function.
98
// Iteration is stopped early if the function returns `false`.
99
// The order of iteration is undefined.
100
fun forEach(function: ((AccountKey): Bool)): Void
101
102
// The total number of unrevoked keys in this account.
103
let count: UInt64
104
}
105
106
struct Inbox {
107
// Publishes a new Capability under the given name, to be claimed by the specified recipient
108
fun publish(_ value: Capability, name: String, recipient: Address)
109
110
// Unpublishes a Capability previously published by this account.
111
// Returns `nil` if no Capability is published under the given name.
112
// Errors if the Capability under that name does not match the provided type.
113
fun unpublish<T: &Any>(_ name: String): Capability<T>?
114
115
// Claims a Capability previously published by the specified provider.
116
// Returns `nil` if no Capability is published under the given name, or if this account is not its intended recipient.
117
// Errors if the Capability under that name does not match the provided type.
118
fun claim<T: &Any>(_ name: String, provider: Address): Capability<T>?
119
}
120
}
121
122
struct DeployedContract {
123
let name: String
124
let code: [UInt8]
125
}

A script can get the AuthAccount for an account address using the built-in getAuthAccount function:

1
fun getAuthAccount(_ address: Address): AuthAccount

This AuthAccount object can perform all operations associated with authorized accounts, and as such this function is only available in scripts, which discard their changes upon completion. Attempting to use this function outside of a script will cause a type error.

Account Creation

Accounts can be created by calling the AuthAccount constructor and passing the account that should pay for the account creation for the payer parameter.

The payer must have enough funds to be able to create an account. If the account does not have the required funds, the program aborts.

1
transaction() {
2
prepare(signer: AuthAccount) {
3
let account = AuthAccount(payer: signer)
4
}
5
}

Account Keys

An account (both PublicAccount and AuthAccount) has keys associated with it. An account key has the following structure.

1
struct AccountKey {
2
let keyIndex: Int
3
let publicKey: PublicKey
4
let hashAlgorithm: HashAlgorithm
5
let weight: UFix64
6
let isRevoked: Bool
7
}

Refer to the PublicKey section for more details on the creation and validity of public keys.

Account Key API

Account key API provides a set of functions to manage account keys.

Add Account Keys

To authorize access to the account, keys can be added using the add() function. Keys can only be added to an AuthAccount.

For example, to create an account and have the signer of the transaction pay for the account creation, and authorize one key to access the account:

1
transaction(publicKey: [UInt8]) {
2
prepare(signer: AuthAccount) {
3
let key = PublicKey(
4
publicKey: publicKey,
5
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
6
)
7
8
let account = AuthAccount(payer: signer)
9
10
account.keys.add(
11
publicKey: key,
12
hashAlgorithm: HashAlgorithm.SHA3_256,
13
weight: 10.0
14
)
15
}
16
}

To add a public key to an existing account, which signed the transaction:

1
transaction(publicKey: [UInt8]) {
2
prepare(signer: AuthAccount) {
3
let key = PublicKey(
4
publicKey: publicKey,
5
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
6
)
7
8
signer.keys.add(
9
publicKey: key,
10
hashAlgorithm: HashAlgorithm.SHA3_256,
11
weight: 10.0
12
)
13
}
14
}

āš ļø Note: Keys can also be added using the addPublicKey function. However, this method is currently deprecated and is available only for the backward compatibility. The addPublicKey method accepts the public key encoded together with their signature algorithm, hashing algorithm and weight.

1
transaction(key: [UInt8]) {
2
prepare(signer: AuthAccount) {
3
let account = AuthAccount(payer: signer)
4
account.addPublicKey(key)
5
}
6
}

Get Account Keys

Keys that are added to an account can be retrieved using get() function, using the index of the key. Revoked keys are always returned, but they have isRevoked field set to true. Returns nil if there is no key available at the given index. Keys can be retrieved from both PublicAccout and AuthAccount.

1
transaction() {
2
prepare(signer: AuthAccount) {
3
// Get a key from an auth account.
4
let keyA = signer.keys.get(keyIndex: 2)
5
6
// Get a key from the public aacount.
7
let publicAccount = getAccount(0x42)
8
let keyB = publicAccount.keys.get(keyIndex: 2)
9
}
10
}

Revoke Account Keys

Keys that have been added to an account can be revoked using revoke() function. Revoke function only marks the key at the given index as revoked, but never deletes it. Keys can only be revoked from an AuthAccount.

1
transaction() {
2
prepare(signer: AuthAccount) {
3
// Get a key from an auth account.
4
let keyA = signer.keys.revoke(keyIndex: 2)
5
}
6
}

āš ļø Note: Keys can also be removed using the removePublicKey function. However, this method is deprecated and is available only for the backward compatibility.

Account Inbox

āš ļø This section describes a feature that is not yet released on Mainnet. It will be available following the next Mainnet Spork.

Accounts also possess an Inbox that can be used to make Capabilities available to specific accounts. The functions in this Inbox provide a convenient means to "bootstrap" Capabilities, setting up an initial connection between two accounts that will later allow them to transfer data or permissions through a Capability.

Publishing a Capability

An account (the provider) that would like to provide a Capability to another account (the recipient) can do so using the publish function:

1
fun publish(_ value: Capability, name: String, recipient: Address)

This publishes the specified Capability using the provided string as an identifier, to be later claimed by the recipient. Note, however, that until the recipient does claim this Capability, it is stored on the provider's account, and contributes towards their Account Storage total.

Calling this function emits an event, InboxValuePublished, that includes the address of both the provider and the recipient, as well as the name and the type of the published Capability. Refer to the Core Events section for more details on this event.

Claiming a Capability

The intended recipient of a Capability can claim that Capability from the provider using the claim function:

1
fun claim<T: &Any>(_ name: String, provider: Address): Capability<T>?

This looks up the specified name in the provider's inbox, returning it to the recipient if it is present, conforms to the provided type argument, and is intended for the calling recipient. If the provider has no Capability stored under the provided name, or if the calling recipient is not the intended recipient of the Capability, the function returns nil. If the borrow type of the Capability is not a subtype of the provided type argument, the function will error at runtime.

Upon successful completion of the claim function, the claimed Capability is removed from the provider's inbox. Note that this means a given Capability can only be claimed once.

Calling this function emits an event, InboxValueClaimed, that includes the address of both the provider and the recipient, as well as the name of the claimed Capability. Refer to the Core Events section for more details on this event.

Unpublishing a Capability

If the provider of a Capability no longer wishes for it to be published for some reason (e.g. they no longer wish to pay for its storage costs), they can unpublish it using the unpublish function:

1
fun unpublish<T: &Any>(_ name: String): Capability<T>?

This looks up the specified name in the provider's inbox, returning it to the provider if it is present and conforms to the provided type argument. If the provider has no Capability stored under the provided name, the function returns nil. If the borrow type of the Capability is not a subtype of the provided type argument, the function will error at runtime.

Upon successful completion of the unpublish function, the unpublished Capability is removed from the provider's inbox.

Calling this function emits an event, InboxValueUnpublished, that includes the address of the provider, and the name of the claimed Capability. Refer to the Core Events section for more details on this event.

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

  • cadenceā€¢fun toString(): String

    Returns the string representation of the path.

    1
    let storagePath = /storage/path
    2
    3
    storagePath.toString() // is "/storage/path"

There are also utilities to produce paths from strings:

1
fun PublicPath(identifier: string): PublicPath?
2
fun PrivatePath(identifier: string): PrivatePath?
3
fun StoragePath(identifier: string): StoragePath?

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

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

  • cadenceā€¢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.

  • cadenceā€¢fun type(at path: 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

  • cadenceā€¢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.

  • cadenceā€¢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.

1
// Declare a resource named `Counter`.
2
//
3
resource Counter {
4
pub var count: Int
5
6
pub init(count: Int) {
7
self.count = count
8
}
9
}
10
11
// In this example an authorized account is available through the constant `authAccount`.
12
13
// Create a new instance of the resource type `Counter`
14
// and save it in the storage of the account.
15
//
16
// The path `/storage/counter` is used to refer to the stored value.
17
// Its identifier `counter` was chosen freely and could be something else.
18
//
19
authAccount.save(<-create Counter(count: 42), to: /storage/counter)
20
21
// Run-time error: Storage already contains an object under path `/storage/counter`
22
//
23
authAccount.save(<-create Counter(count: 123), to: /storage/counter)
24
25
// Load the `Counter` resource from storage path `/storage/counter`.
26
//
27
// The new constant `counter` has the type `Counter?`, i.e., it is an optional,
28
// and its value is the counter resource, that was saved at the beginning
29
// of the example.
30
//
31
let counter <- authAccount.load<@Counter>(from: /storage/counter)
32
33
// The storage is now empty, there is no longer an object stored
34
// under the path `/storage/counter`.
35
36
// Load the `Counter` resource again from storage path `/storage/counter`.
37
//
38
// The new constant `counter2` has the type `Counter?` and is `nil`,
39
// as nothing is stored under the path `/storage/counter` anymore,
40
// because the previous load moved the counter out of storage.
41
//
42
let counter2 <- authAccount.load<@Counter>(from: /storage/counter)
43
44
// Create another new instance of the resource type `Counter`
45
// and save it in the storage of the account.
46
//
47
// The path `/storage/otherCounter` is used to refer to the stored value.
48
//
49
authAccount.save(<-create Counter(count: 123), to: /storage/otherCounter)
50
51
// Load the `Vault` resource from storage path `/storage/otherCounter`.
52
//
53
// The new constant `vault` has the type `Vault?` and its value is `nil`,
54
// as there is a resource with type `Counter` stored under the path,
55
// which is not a subtype of the requested type `Vault`.
56
//
57
let vault <- authAccount.load<@Vault>(from: /storage/otherCounter)
58
59
// The storage still stores a `Counter` resource under the path `/storage/otherCounter`.
60
61
// Save the string "Hello, World" in storage
62
// under the path `/storage/helloWorldMessage`.
63
64
authAccount.save("Hello, world!", to: /storage/helloWorldMessage)
65
66
// Copy the stored message from storage.
67
//
68
// After the copy, the storage still stores the string under the path.
69
// Unlike `load`, `copy` does not remove the object from storage.
70
//
71
let message = authAccount.copy<String>(from: /storage/helloWorldMessage)
72
73
// Create a new instance of the resource type `Vault`
74
// and save it in the storage of the account.
75
//
76
authAccount.save(<-createEmptyVault(), to: /storage/vault)
77
78
// Invalid: Cannot copy a resource, as this would allow arbitrary duplication.
79
//
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:

  • cadenceā€¢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.

1
// Declare a resource interface named `HasCount`, that has a field `count`
2
//
3
resource interface HasCount {
4
count: Int
5
}
6
7
// Declare a resource named `Counter` that conforms to `HasCount`
8
//
9
resource Counter: HasCount {
10
pub var count: Int
11
12
pub init(count: Int) {
13
self.count = count
14
}
15
}
16
17
// In this example an authorized account is available through the constant `authAccount`.
18
19
// Create a new instance of the resource type `Counter`
20
// and save it in the storage of the account.
21
//
22
// The path `/storage/counter` is used to refer to the stored value.
23
// Its identifier `counter` was chosen freely and could be something else.
24
//
25
authAccount.save(<-create Counter(count: 42), to: /storage/counter)
26
27
// Create a reference to the object stored under path `/storage/counter`,
28
// typed as `&Counter`.
29
//
30
// `counterRef` has type `&Counter?` and is a valid reference, i.e. non-`nil`,
31
// because the borrow succeeded:
32
//
33
// There is an object stored under path `/storage/counter`
34
// and it has type `Counter`, so it can be borrowed as `&Counter`
35
//
36
let counterRef = authAccount.borrow<&Counter>(from: /storage/counter)
37
38
counterRef?.count // is `42`
39
40
// Create a reference to the object stored under path `/storage/counter`,
41
// typed as `&{HasCount}`.
42
//
43
// `hasCountRef` is non-`nil`, as there is an object stored under path `/storage/counter`,
44
// and the stored value of type `Counter` conforms to the requested type `{HasCount}`:
45
// the type `Counter` implements the restricted type's restriction `HasCount`
46
47
let hasCountRef = authAccount.borrow<&{HasCount}>(from: /storage/counter)
48
49
// Create a reference to the object stored under path `/storage/counter`,
50
// typed as `&{SomethingElse}`.
51
//
52
// `otherRef` is `nil`, as there is an object stored under path `/storage/counter`,
53
// but the stored value of type `Counter` does not conform to the requested type `{Other}`:
54
// the type `Counter` does not implement the restricted type's restriction `Other`
55
56
let otherRef = authAccount.borrow<&{Other}>(from: /storage/counter)
57
58
// Create a reference to the object stored under path `/storage/nonExistent`,
59
// typed as `&{HasCount}`.
60
//
61
// `nonExistentRef` is `nil`, as there is nothing stored under path `/storage/nonExistent`
62
//
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:

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

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:

1
account.save(1, to: /storage/foo1)
2
account.save(2, to: /storage/foo2)
3
account.save(3, to: /storage/foo3)
4
account.save("qux", to: /storage/foo4)
5
6
account.forEachStored(fun (path: StoragePath, type: Type): Bool {
7
if type == Type<String>() {
8
account.save("bar", to: /storage/foo5)
9
// returning false here ends iteration after storage is modified, preventing a panic
10
return false
11
}
12
return true
13
})

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:

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