Skip to main content
Version: 1.0

Type Annotations in Cadence 1.0

In addition to updating your contracts in reaction to the Core Contract changes in Cadence 1.0, certain language changes in Cadence 1.0 will also require changes to type annotations in your contracts, in particular type annotations on resource and struct fields. These type updates are required to accurately reflect the way that the Cadence 1.0 data migrations will change the types of these fields' values, and the Cadence 1.0 upgrade validator will enforce that these upgrades are accurate.

Restricted Types​

In Cadence 1.0, support for restricted types was dropped, and replaced with intersection types. As such, any existing restricted types must be replaced with a different type. During the automated state migration for the Cadence 1.0 upgrade, restricted typed-values will be migrated according to a specific set of rules, and all developers must update the types in their contracts to mirror this.

  • AnyStruct{I} and AnyResource{I} should be migrated to just {I}, as these types have identical behavior
  • For any other type T, T{I} should be migrated to T, as this is the most specific possible type that can go here
  • For any type T, T{} should be migrated to just T, as there is no support for empty intersection types

So, for example, a value of type FlowToken.Vault{FungibleToken.Receiver} should be migrated to just a FlowToken.Vault type, while a value of type AnyResource{Provider, Receiver} should be migrated to a {Provider, Receiver} intersection.

Reference Types​

Reference types (whether on their own like &FlowToken.Vault or within a capability type like Capability<&FlowToken.Vault{FungibleToken.Provider}>) from contracts written in Cadence v0.42 will need to be given entitlements in order to retain the same functionality in Cadence 1.0. The Cadence 1.0 automated data migration will automatically grant the appropriate entitlements to stored values, but any reference types that appear in your contracts will need to be manually updated.

The update you will need to perform involves changing each reference type to have the appropriate entitlements necessary to perform the same operations in Cadence 1.0 that it previously could in Cadence v0.42. The Cadence 1.0 upgrade validator will enforce that these upgrades are accurate, and will suggest the correct type in case of an error. However, if you'd like to understand how the validator computes this type, the next section has a technical explanation of what the validator is computing.

How the Validator Computes Entitlements​

The first basic concept necessary to understand the upgrade is the "entitlements function"; i.e. a hypothetical function that computes the set of entitlements that are valid for some composite or interface type T. This is just the complete set of entitlements that appear in that type's definition.

E.g., for a resource type R defined as:


_10
access(all) resource R {
_10
access(E) fun foo() { ... }
_10
access(G | H) fun bar() { ... }
_10
}

Entitlements(I) would be equal to {E, G, H}, i.e. all the entitlements that appear in I's definition. The idea here is that any reference that was previously typed as &R was originally able to call all the pub functions in R (here both foo and bar), and after the Cadence 1.0 migration we want that to still be the case. In order to make that true, we need to give the &R all the entitlements it might need to do that, which is exactly Entitlements(R).

All of which is to say, any &R reference types that appear in your contract must be updated to auth(E1, E2, ...) &R, where E1, E2, ... are all the entitlements in Entitlements(R).

One important note is that reference to restricted types (&T{I}) behave slightly differently; instead of being given entitlements to T, they are instead granted only entitlements based on the interfaces in the restriction set (here {I}). So for some interface and composite defined like so:


_10
access(all) resource interface I {
_10
access(E) fun foo()
_10
}
_10
access(all) resource R: I {
_10
access(E) fun foo() { ... }
_10
access(F) fun bar() { ... }
_10
}

A type &R{I} should be updated to auth(E) &R, since the entitlements it is given is only those in I. It does not receive an entitlement to F, since the old &R{I} was not able to call bar.