Update 9th June
Hi Everyone, we have released the latest versions of all developer tools updated to Secure Cadence:
Update 10th May
Hi Everyone, we wanted to let you all know that our amazing community member Jacob recorded this video to help you with updating your contracts, definitely work watching!
Hello developers,
We want to make everyone aware that there are breaking changes planned for the upcoming June Testnet & Mainnet Spork that will impact the majority of smart contracts deployed. These changes represent a critical step in the Path to Stable Cadence and Permissionless Deployment, a milestone we’re calling Secure Cadence.
The Secure Cadence milestone is focused on code-hardening and will enable the deployment of smart contracts on Mainnet. A future update, called Stable Cadence, will focus on usability improvements. Splitting these into two milestones reduces the complexity, which helps spread the lift required from teams impacted by these changes.
Please review this critical information below.
Key Dates
Disclaimer: These dates could change pending any findings during the ongoing securuity auditing and testing of the Cadence release candidate
- Testnet Deployment: June 8: We are shipping these changes to Testnet a week earlier, so that developers can test these changes
- Mainnet Deployment: June 15
Note that the time between the sporks is longer than usual.
During this time Testnet and Mainnet will run two different, incompatible versions of Cadence.
What You Need To Do
EVERYONE who has contracts on Testnet and Mainnet NEEDS to follow these steps to make sure your contracts are ready for the breaking changes.
- Flow core contributors created a tool which you can use to find breaking changes
- Test your contracts with the latest Emulator build
v0.33.1-sc-m5
, which includes the Secure Cadence changes:-
To test with the CLI: Install the Flow CLI with an integrated Secure Cadence Emulator (see more info on Emulator in the Emulator documentation):
-
On Linux and macOS:
sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)"
-
On Windows, in PowerShell:
iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') }"
-
-
To test with the SDK with an integrated Emulator: Get the Emulator with secure cadence here. This uses these special versions of Cadence, the Emulator, and the SDK:
-
github.com/onflow/cadence v0.24.1
github.com/onflow/flow-emulator v0.33.1
github.com/onflow/flow-go-sdk v0.26.1
-
Keep a local build ready to test end-to-end changes on Testnet after June 8th
-
After the Mainnet deployment, the network will be permissionless for all existing contracts - this means you will be able to update your contracts immediately after the network is up after the spork without requiring any voucher or review. If you have identified any impact then you will need to upgrade those contracts (You will be able to do that by yourself and without any approval or review from the Flow core team).
Note: Permissionless deployments of new contracts will be enabled shortly after the mainnet spork. See how to update:
If you need any help with the code review and impact analysis, please leave a question on the forums, or reach out for help on the Flow Discord.
Please review the list of Breaking Changes below.
Breaking Changes
Taking a reference to an optional value now produces an optionally-typed reference
Can be identified via analysis tool.
-
Change description:
Taking a reference to an optional value will now produce an optionally-typed reference instead of producing a checker error. For example:
let x: Int? = 1 let y = &x as &Int? // y has type (&Int)?
Additionally, taking a reference to a dictionary element will now produce an optional reference instead of a normal reference:
let dict: {String: Int} = {} let s = &dict[""] as &Int? // s now has type (&Int)? let z = &dict[""] as &Int // This is a type error
This change will make it less likely for developers to accidentally create a reference to
nil
by referencing a dictionary element that is not present. More detail on the reasoning behind this change can be found in this FLIP: https://github.com/onflow/flow/pull/722 -
Update instructions: Developers will need to add optional
?
markers to their dictionary references to comply with the new typing rules. They can replicate the old behavior by using the!
forcing operator on the reference value. However, it is recommended that developers properlynil
check their references instead of forcing them. For example, code that used to look like this:pub fun getNFTReference(id: UInt64): &NonFungibleToken.NFT { return &self.nfts[id] as &NonFungibleToken.NFT }
can now be rewritten as:
pub fun getNFTReference(id: UInt64): &NonFungibleToken.NFT { return (&self.nfts[id] as &NonFungibleToken.NFT?)! }
to satisfy the stricter type checking. However, to benefit from the increased safety of this change, it would be preferred to write the above code this way instead:
pub fun getNFTReference(id: UInt64): &NonFungibleToken.NFT? { return &self.nfts[id] as &NonFungibleToken.NFT? }
and then
nil
-checking the result ofgetNFTReference
before using it.
Mutation of container-typed fields is now limited to the scope of the enclosing composite
Can be identified via analysis tool.
-
Change description:
Array and dictionary fields of
struct
andresource
values can no longer be modified outside of thestruct
orresource
value in which they were defined. Attempting to do so is now a type error. This includes both direct modification with indexed assignment (e.g.arr[0] = 3
) as well as certain methods which mutate the array/dictionary. The methods that are considered mutating are noted in the documentation. So, as an example, the following would now produce a type error:pub struct Foo { pub let y: [Int] init() { self.y = [3] } } pub fun main() { let foo = Foo() foo.y.append(1) // invalid, field `y` cannot be modified outside of `Foo` }
Fields declared with
pub(set) var
are still mutable in any context.This change is designed to make it less likely for developers to accidentally allow users to modify the internal state of their contracts or resources when they only intended to allow this state to be read. More detail and discussion on the motivation for this change can be found here: https://github.com/onflow/flow/pull/703
-
Update instructions:
Contract authors will need to audit the places in their contracts, structs and resources where they have
pub
visibility fields. If they do not want other contracts or transactions to be able to modify these fields, no action will be necessary. If they do wish for modification to be possible, they will need to add setter or modifier methods to allow this. For example:pub struct Foo { pub let y: [Int] init() { self.y = [3] } pub fun appendToArray(_ x: Int) { self.y.append(x) } } pub fun main() { let foo = Foo() foo.appendToArray(1) // ok }
Contract and transaction authors will also need to find the places where the new mutation type error occurs in their code, and either rewrite these portions to use any modifier or setter methods that the contract author chose to provide, or to not modify the internal state of other contracts.
The load
, copy
and borrow
functions now perform a force cast
Can be identified via analysis tool.
- Change description:
load
copy
andborrow
no longer returnnil
when the type of the value in storage does not match the supplied argument. Instead, a runtime error is produced. More information about this change can be found in this FLIP: https://github.com/onflow/flow/pull/718 - Update instructions: Developers will need to audit the uses of these functions to ensure they are never called with the incorrect type. In locations where developers wanted the
nil
returning behavior if the stored value had the wrong type, they can use the newtype
function onAuthAccount
to check the type of a value in storage (https://docs.onflow.org/cadence/language/accounts/#account-storage-api) - https://github.com/onflow/cadence/pull/1286
Missing commas in parameter lists are now reported as an error
Can be identified via analysis tool.
-
Change description:
The parser ignored missing commas in parameter lists.
The parser now reports an error when the comma is missing.
For example, the following program was accepted, but should have not, and is now rejected:fun test(a: Int b: String) {} // ^ missing comma between parameter `a` and `b`
-
Update instructions:
Ensure parameter lists contain commas where necessary.
String representations of addresses are now zero-padded
Can be identified via analysis tool.
-
Change description:
Converting an address to a string now zero-pads the result.
For example:let address: Address = 0x1 address.toString() // results now in "0x0000000000000001"
-
Update instructions:
Ensure your code does not rely on the non-padded representation of addresses
The type checker now infers the common super-type for expressions
Can be identified via analysis tool.
-
Change description:
Cadence type checker is now capable of inferring types for array literals, dictionary literals, and conditional expressions, by taking the super-type of each sub-expression (i.e: each element of array, dictionary). For example:
var x = [1, 6, 35] // x has type [Int] var y = [1, "hello", true] // y has type [AnyStruct]
-
Update instructions:
With this change, array literals and dictionary literals no longer use the first element to infer the type for the literal. Thus, if the literal is required to have a certain specific type, then explicit type annotation or casting must be used for the entire expression, rather than the first element.
For example: To annotate a number array as
[UInt8]
:Old way:
var x = [1 as UInt8, 6, 35]
New way:
var x = [1, 6, 35] as [UInt8] or var x: [UInt8] = [1, 6, 35]
Arithmetic, comparison ,and bitwise operations on number super-types are now invalid
Can be identified via analysis tool.
-
Change description:
Statically disallowed arithmetic, comparison and bitwise operations on numeric super-types, as they may or may not succeed at run-time. The idea was to make the current behaviour that happens under the hood, more explicit to the developers, and by doing so, help them to avoid any unintentional/unforeseen behaviours.
e.g:
let x: Integer = 3 as Int8 let y: Integer = 4 as Int8 let z: Integer = x + y // This will result in a static type checking error
More details can be found in this FLIP.
-
Update instructions:
Developers will have to explicitly cast it to the desired type before they do the arithmetic/comparisons operation on number super-types.
The updated code to achieve the same above example:
let x: Integer = 3 as Int8 let y: Integer = 4 as Int8 let z: Integer = (x as! Int8) + (y as! Int8) // Cast to the specific type // before the operation.
The representation of character values was fixed
Cannot be identified via static code analysis tool.
-
Change description:
Adds a runtime value for the
Character
type. Indexing into a string (e.g.str[0]
) will now produce aCharacter
value, rather than a string, and developers can declareCharacter
values from string literals that are exactly one character long, like so:let a: Character = "ĂĽ" let b: Character = "\u{FC}" let c: Character = "\u{75}\u{308}" let d: Character = "y" let type = a.getType() // now returns Type<Character>() instead of incorrect Type<String>()
-
Update instructions:
Character
values can be converted to strings using the.toString()
method; any type errors that may arise as a result of certain operations producingCharacter
values instead of strings can be fixed by calling this method on theCharacter
in question.
The representation of types in JSON data changed
Cannot be identified via static code analysis tool.
-
Change description:
The representation of types in capabilities and type values in JSON data changed from a string to a structured format.
This only affects off-chain code which e.g. uses the result of a script.
This does not affect Cadence transactions or contracts.For example, previously a capability had a JSON property
borrowType
, which was e.g."&AnyResource{A.ee82856bf20e2aa6.FungibleToken.Receiver}"
This type information is now structured as a JSON object, e.g.
{ "authorized": false, "kind": "Reference", "type": { "kind": "Restriction", "restrictions": { // ...