Breaking changes coming with Secure Cadence release!

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)" -- v0.33.1-sc-m5
        
      • On Windows, in PowerShell:

        iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') } -Version v0.33.1-sc-m5"
        
    • 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.21.3-0.20220419065337-d5202c162010
      github.com/onflow/flow-emulator v0.31.2-0.20220422152808-6c116a9d88b7
      github.com/onflow/flow-go-sdk v0.24.1-0.20220421152843-9ce4d554036e
      
  • 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.

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

:bulb: 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 properly nil check their references instead of forcing them. For example, code that used to look like this:

    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
        return &self.ownedNFTs[id] as &NonFungibleToken.NFT
    }
    

    can now be rewritten as:

    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
    	return (&self.ownedNFTs[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 borrowNFT(id: UInt64): &NonFungibleToken.NFT? {
    	return &self.ownedNFTs[id] as &NonFungibleToken.NFT?
    }
    

    and then nil-checking the result of borrowNFT before using it.

  • https://github.com/onflow/cadence/pull/1303

Mutation of container-typed fields is now limited to the scope of the enclosing composite

:bulb: Can be identified via analysis tool.

  • Change description:

    Array and dictionary fields of struct and resource values can no longer be modified outside of the struct or resource 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.

  • https://github.com/onflow/cadence/pull/1267

The load, copy and borrow functions now perform a force cast

:bulb: Can be identified via analysis tool.

Missing commas in parameter lists are now reported as an error

:bulb: 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.

  • https://github.com/onflow/cadence/pull/1073

String representations of addresses are now zero-padded

:bulb: 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

  • https://github.com/onflow/cadence/pull/1214

The type checker now infers the common super-type for expressions

:bulb: 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]
    
  • https://github.com/onflow/cadence/pull/1027

Arithmetic, comparison ,and bitwise operations on number super-types are now invalid

:bulb: 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.
    
  • https://github.com/onflow/cadence/pull/1360

The representation of character values was fixed

:warning: 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 a Character value, rather than a string, and developers can declare Character 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 producing Character values instead of strings can be fixed by calling this method on the Character in question.

  • https://github.com/onflow/cadence/pull/1392

The representation of types in JSON data changed

:warning: 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": { 
                // ...
    
  • https://github.com/onflow/cadence/pull/1202

3 Likes

Hi,

Scuse me but when I want install the Flow CLI with an integrated Secure Cadence Emulator and I run your command (for macos):
sh -ci "$(curl -fsSL [https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh](https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh))" -- v0.33.1-sc-m5

I have this error :
zsh: unknown file attribute: h

Can you help me please ?
For information I have already flow-cli installed on version v0.33.1. May be it’ s already ok for my flow-cli ? I would like to be sure to have secure Cadence Emulator integrated ?

Thanks.

Hi @sylv1un, somehow markdown sneaked into the install command, this was fixed now, please try again!

Regards,

Jan

It is perfect !! Thank you :ok_hand:

1 Like

Does the force casting only concern the borrow() method of AuthAccount, or also the same method of a Capability? It seems to be the former from a first test.

The Flow core contributors created a tool which you can use to analyze your Cadence contracts, check it out here: Secure Cadence Breaking Changes Analyzer

Hi,

I have 2 questions ( at the moment :wink: ) :
1) if I deploy my contracts and run transactions correctly without any problem on emulator with last version v0.33.1, does this mean that I will have no problem with my contracts concerning the changes upcomming ?

2) When I run the “Secure Cadence Breaking Changes Analyzer” (to test this tool) on the NFTStorefront contract I get severals errors :
for example :
error: check required: storage read operations will perform a force-cast

cut.receiver.borrow()
| ^^^^^^^^^^^^^^^^^^^^^ ensure the new behaviour is supported
at the line 165.

Another example :
error: update required: reference to optional will return optional reference

return &self.listings[listingResourceID] as! &Listing{ListingPublic}
| ^^^^^^^^^^^^^^^^^^^^^^^ replace with '&Listing{ListingPublic}?'
at the line 456

When I check the “NFTStorefront.cdc” mainnet contract, I don’t see any changes. Does this mean that these modifications are not necessary or that they will be made later ?

Thanks for your help :slightly_smiling_face:

Thanks for the amazing work. I am trying to fix the breaking changes in my code, but don’t understand why this fails when deploying my contracts (vscode does not show any errors).

pub fun borrowBid(id: UInt64): &BidCollection.Bid {
    pre {
        self.bids[id] != nil: "Bid with given id was not found"
    }
    return (&self.bids[id] as &BidCollection.Bid?)!
}

however, I am getting following error which makes no sense:

error: cannot create reference
   --> f8d6e0586b0a20c7.BidCollection:117:38
    |
117 |             return (&self.bids[id] as &BidCollection.Bid?)!
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference type, got `&BidCollection.Bid?`

here is the signature of Bid:

pub resource Bid: BidDetails, BidAccept

and self.bids:

pub let bids: @{UInt64: BidCollection.Bid} 

No, as pointed out above, you must test with version v0.33.1-sc-m5.

Until the spork, contracts cannot be updated yet, they must be updated after spork.

Contracts like NFTStorefront also need to get updated.

The update looks good. Are you sure you are testing the updated code with the Secure Cadence emulator? Note that you must use version v0.33.1-sc-m5

Weird. I checked flow version (was v0.33.1-sc-m5), re-started my machine, re-installed vscode extension etc. earlier, but it did not work. I tried to delete everything related to flow now, re-installed then it worked.

1 Like

Ok thank you very much for this perfect response. :+1: :pray:

I am getting this weird error when passing params of Capability:

The code is here if you want to test: https://github.com/onkel-skrue/test-secure-cadence/blob/main/cadence/Test.cdc#L10

Edit: I can still deploy the contract.
Edit: Found out that this is known and will be fixed next week according to Discord.

Heya folks! is there a change in secure cadence when I want to typecast the UFix64 to UInt64? I know there is a change when doing arithmetic, comparison, and bitwise operation. Right now below is the statement that I am using in my pre-condition

expiry > UInt64(getCurrentBlock().timestamp) : "Expiry should be in future"

It is working fine, but as per the secure cadence do I have to change it? I am using flow CLI version - Version: v0.33.1-sc-m5 and latest flow-js-testing library i.e ^0.2.3-alpha.6

To clarify, UInt64(number) calls the number conversion function UInt64, this is not type casting – type casting is done using the as/as?/as! operators.

Neither number conversion functions nor type casts have changed.

If you see any problems with these, please let us know.

1 Like

Was wondering if this known bug relating to dependency tracking of imports in the language server/vs code has been addressed yet, as it is still causing me issues?

image

The impact of this is that any of my contracts that include the FungibleToken.Receiver restricting type cannot be imported into any other contracts. The vs code extension identifies the problem as “checking of imported program ./<contract-name>.cdc failed”.

Since the imports on my contracts are broken, by extension, the imports on my txs and scripts are also broken. This means that I when I run my flow-js-testing test suite, all tests that involve these erroring txs/scripts/contracts fail, where previously (Flow CLI v0.33.1) they didn’t.

Here is the Discord thread that raises the problem.

My versions:

Flow CLI: v0.33.1-sc-m6
flow-js-testing: ^0.2.3-alpha.5

Thanks!

@phill apologies for the problems with the vscode, can you try to install the CLI version v0.33.2-sc-m6.

is the emulator network also not updated? so we cannot run analyzer on emulator-deployed contracts?

i am on Version: v0.33.1-sc-m6, but am still seeing the same errors as @phill . I have closed on opened vscode multiple times

@Babayaga can you try the v0.33.2-sc-m6 version as mentioned above.