diff --git a/docs/build/advanced-concepts/account-linking/index.md b/docs/build/advanced-concepts/account-linking/index.md index f7aa1f3922..98e8f13233 100644 --- a/docs/build/advanced-concepts/account-linking/index.md +++ b/docs/build/advanced-concepts/account-linking/index.md @@ -19,7 +19,7 @@ Since access to the `AuthAccount` object enables state change, the idea of accou ## AuthAccount Capabilities -Before proceeding the reader will need a clear understanding of [Cadence capabilities](https://developers.flow.com/cadence/language/capabilities.md) to follow this section. Advanced features such as AuthAccount Capabilities are powerful but if used incorrectly can put your app or users at risk. +Before proceeding the reader will need a clear understanding of [Cadence capabilities](https://cadence-lang.org/docs/language/capabilities) to follow this section. Advanced features such as AuthAccount Capabilities are powerful but if used incorrectly can put your app or users at risk. Cadence allows the creation of capabilities to delegate access to account storage, meaning any account that has a valid capability to another account object in the storage can access it. This is a powerful feature on its own. The access to the object can be limited when creating a capability so only intended functions or fields can be accessed. @@ -106,17 +106,17 @@ Apps need assurances that their own resources are safe from malicious actors, so Learn more about it in the [Hybrid Custody documentation](./parent-accounts.md). -### Guides[](https://developers.flow.com/concepts/account-linking#guides) +### Guides -- [Building Walletless Applications Using Child Accounts](https://developers.flow.com/concepts/account-linking/child-accounts) covers how apps can leverage Account Linking to create a seamless user experience and enable future self-custody. -- [Working With Parent Accounts](https://developers.flow.com/concepts/account-linking/parent-accounts) covers features enabled by the core `HybridCustody` contract to access child account assets from parent accounts. This is useful for apps like marketplaces or wallets that are working with accounts that have potential child accounts. +- [Building Walletless Applications Using Child Accounts](./child-accounts.md) covers how apps can leverage Account Linking to create a seamless user experience and enable future self-custody. +- [Working With Parent Accounts](./parent-accounts.md) covers features enabled by the core `HybridCustody` contract to access child account assets from parent accounts. This is useful for apps like marketplaces or wallets that are working with accounts that have potential child accounts. -### Resources[](https://developers.flow.com/concepts/account-linking#resources) +### Resources - [Forum Post](https://forum.onflow.org/t/hybrid-custody/4016) where core concepts were introduced and discussed. - [GitHub repository](https://github.com/onflow/hybrid-custody) where `HybridCustody` core contract is maintained. Check out the repository for more advanced script or transaction examples. - [Example](https://github.com/jribbink/magic-link-hc-sample/) Account Linking project with [Magic](https://magic.link/). - [Starter template](https://github.com/Niftory/niftory-samples/tree/main/walletless-onboarding) for [Niftory](https://niftory.com/) Account Linking API. -- [Scaffold](https://github.com/onflow/hybrid-custody-scaffold) to start a new Account Linking project. Check out Flow CLI [Scaffolds](https://developers.flow.com/tooling/flow-cli/super-commands#using-scaffolds) to get started. +- [Scaffold](https://github.com/onflow/hybrid-custody-scaffold) to start a new Account Linking project. Check out Flow CLI [Scaffolds](../../../tools/flow-cli/super-commands.md#using-scaffolds) to get started. - [Wallet API](https://github.com/flow-hydraulics/flow-wallet-api/) is a microservice that enables apps to custody accounts. This is useful if you don't want to use a third-party service to manage user accounts. - [Walletless Arcade](https://github.com/onflow/walletless-arcade-example) example demonstrates Account Linking with a local key management solution. \ No newline at end of file diff --git a/docs/build/advanced-concepts/account-linking/parent-accounts.md b/docs/build/advanced-concepts/account-linking/parent-accounts.md index 2cca27577d..46e1cc95c2 100644 --- a/docs/build/advanced-concepts/account-linking/parent-accounts.md +++ b/docs/build/advanced-concepts/account-linking/parent-accounts.md @@ -35,8 +35,7 @@ access between the app - the entity which created likely custodies the account - How does this delegation occur? Typically when we think of shared account access in crypto, we think keys. However, Cadence enables [accounts to link Capabilities to themselves](https://github.com/onflow/cadence/issues/2151) and issue -those Capabilities to other parties (more on [capability-based access -here](https://developers.flow.com/cadence/language/capabilities.md)). +those Capabilities to other parties (more on [capability-based access here](https://cadence-lang.org/docs/language/capabilities)). We've leveraged this feature in a (proposed) standard so that apps can implement a hybrid custody model whereby the app creates an account it controls, then later delegates access on that account to the user once they've authenticated diff --git a/docs/build/advanced-concepts/randomness.md b/docs/build/advanced-concepts/randomness.md index 63a9ec734d..051ae0a593 100644 --- a/docs/build/advanced-concepts/randomness.md +++ b/docs/build/advanced-concepts/randomness.md @@ -43,7 +43,7 @@ For usage of randomness where result abortion is not an issue, it is recommended ```cadence // Language reference: -// https://developers.flow.com/cadence/language/built-in-functions#revertiblerandom +// https://cadence-lang.org/docs/language/built-in-functions#revertiblerandom // Run the snippet here: https://academy.ecdao.org/en/snippets/cadence-random access(all) fun main(): UInt64 { // Simple assignment using revertibleRandom - keep reading docs for safe usage! diff --git a/docs/build/basics/accounts.md b/docs/build/basics/accounts.md index 3079c3dee4..1a6ae57427 100644 --- a/docs/build/basics/accounts.md +++ b/docs/build/basics/accounts.md @@ -170,7 +170,7 @@ Find [more about the command in the CLI docs](../../tools/flow-cli/accounts/get- Accounts can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: -**gRPC API** [building-on-flow/nodes/access-api#accounts](../../references/run-and-secure/nodes/access-api.mdx#accounts) +**gRPC API** [building-on-flow/nodes/access-api#accounts](../../references/run-and-secure/nodes/access-api.md#accounts) **REST API** [http-api#tag/Accounts](/http-api#tag/Accounts) diff --git a/docs/build/basics/blocks.md b/docs/build/basics/blocks.md index 8fe8dc4f09..d3451672b2 100644 --- a/docs/build/basics/blocks.md +++ b/docs/build/basics/blocks.md @@ -63,7 +63,7 @@ Find [more about the command in the CLI docs](../../tools/flow-cli/get-flow-data Blocks can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: -[**gRPC Block API**](../../references/run-and-secure/nodes/access-api.mdx#blocks) +[**gRPC Block API**](../../references/run-and-secure/nodes/access-api.md#blocks) [**REST Block API**](/http-api#tag/Blocks) diff --git a/docs/build/basics/collections.md b/docs/build/basics/collections.md index 6d32f09aa0..39f9141a8a 100644 --- a/docs/build/basics/collections.md +++ b/docs/build/basics/collections.md @@ -20,7 +20,7 @@ Find [more about the command in the CLI docs](../../tools/flow-cli/get-flow-data Collections can be obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: -[**gRPC Collection API**](../../references/run-and-secure/nodes/access-api.mdx#collections) +[**gRPC Collection API**](../../references/run-and-secure/nodes/access-api.md#collections) [**REST Collection API**](/http-api#tag/Collections) diff --git a/docs/build/basics/fees.md b/docs/build/basics/fees.md index 1d5fea2126..94f74a4410 100644 --- a/docs/build/basics/fees.md +++ b/docs/build/basics/fees.md @@ -294,7 +294,7 @@ pub fun add(_ a: Int, _ b: Int): Int { **Avoid excessive load and save operations** -Avoid costly loading and storage operations and [borrow references](https://cadence-lang.org/docs/design-patterns#avoid-excessive-load-and-save-storage-operations-prefer-in-place-mutations) where possible, for example: +Avoid costly loading and storage operations and [borrow references](../best-practices/design-patterns.md#avoid-excessive-load-and-save-storage-operations-prefer-in-place-mutations) where possible, for example: ```cadence transaction { diff --git a/docs/build/basics/scripts.md b/docs/build/basics/scripts.md index 946a88590f..4205e34f2f 100644 --- a/docs/build/basics/scripts.md +++ b/docs/build/basics/scripts.md @@ -46,7 +46,7 @@ Scripts can be run against previous blocks, allowing you to query historic data Scripts are executed by being submitted to the Access Node APIs. Currently, there’s support for two APIs: -[**gRPC Script API**](../../references/run-and-secure/nodes/access-api.mdx#scripts) +[**gRPC Script API**](../../references/run-and-secure/nodes/access-api.md#scripts) [**REST Script API**](/http-api#tag/Scripts) diff --git a/docs/build/basics/transactions.md b/docs/build/basics/transactions.md index 9d5be30b0e..4eb65d240d 100644 --- a/docs/build/basics/transactions.md +++ b/docs/build/basics/transactions.md @@ -40,7 +40,7 @@ transaction(greeting: String) { **Arguments** -Transactions may declare parameters it needs during execution, these must be provided as input arguments when sending a transaction. You can think of them as function arguments. Currently, we provide [arguments in the JSON-Cadence Data Interchange Format](https://cadence-lang.org/docs/json-cadence-spec#docusaurus_skipToContent_fallback). Which is a human-readable JSON format. The sample script from above accepts a single `String` argument. +Transactions may declare parameters it needs during execution, these must be provided as input arguments when sending a transaction. You can think of them as function arguments. Currently, we provide [arguments in the JSON-Cadence Data Interchange Format](../cadence-reference/json-cadence-spec.md). Which is a human-readable JSON format. The sample script from above accepts a single `String` argument. **Reference Block** @@ -376,7 +376,7 @@ A user can define their own transactions or it can use already defined transacti Transactions can be submitted and obtained from the access node APIs, currently, there are two gRPC and REST APIs. You can find more information about them here: -[**gRPC Transaction API**](../../references/run-and-secure/nodes/access-api.mdx#transactions) +[**gRPC Transaction API**](../../references/run-and-secure/nodes/access-api.md#transactions) [**REST Transaction API**](/http-api#tag/Transactions) diff --git a/docs/build/best-practices/_category_.yml b/docs/build/best-practices/_category_.yml new file mode 100644 index 0000000000..a891256bef --- /dev/null +++ b/docs/build/best-practices/_category_.yml @@ -0,0 +1 @@ +label: Basic Practices \ No newline at end of file diff --git a/docs/build/best-practices/anti-patterns.md b/docs/build/best-practices/anti-patterns.md new file mode 100644 index 0000000000..ad0070ae1f --- /dev/null +++ b/docs/build/best-practices/anti-patterns.md @@ -0,0 +1,384 @@ +--- +title: Cadence Anti-Patterns +sidebar_position: 2 +sidebar_label: Anti-Patterns +--- + +:::info +This is an opinionated list of issues that can be improved if they are found in Cadence code intended for production. +::: + +# Security and Robustness + +## Avoid using `AuthAccount` as a function parameter + +### Problem + +Some may choose to authenticate or perform operations for their users by using the users' account addresses. +In order to do this, a commonly seen case would be to pass the user's `AuthAccount` object +as a parameter to a contract function to use for querying the account or storing objects directly. +This is problematic, as the `AuthAccount` object allows access to ALL private areas of the account, +for example, all of the user's storage, authorized keys, etc., +which provides the opportunity for bad actors to take advantage of. + +### Example: + +```cadence +... +// BAD CODE +// DO NOT COPY + +// Imagine this code is in a contract that uses AuthAccount to authenticate users +// To transfer NFTs + +// They could deploy the contract with an Ethereum-style access control list functionality + +pub fun transferNFT(id: UInt64, owner: AuthAccount) { + assert(owner(id) == owner.address) + + transfer(id) +} + +// But they could upgrade the function to have the same signature +// so it looks like it is doing the same thing, but they could also drain a little bit +// of FLOW from the user's vault, a totally separate piece of the account that +// should not be accessible in this function +// BAD + +pub fun transferNFT(id: UInt64, owner: AuthAccount) { + assert(owner(id) == owner.address) + + transfer(id) + + // Sneakily borrow a reference to the user's Flow Token Vault + // and withdraw a bit of FLOW + // BAD + let vaultRef = owner.borrow<&FlowToken.Vault>(/storage/flowTokenVault)! + let stolenTokens <- vaultRef.withdraw(amount: 0.1) + + // deposit the stolen funds in the contract owners vault + // BAD + contractVault.deposit(from: <-stolenTokens) +} +... +``` + +### Solution + +Projects should find other ways to authenticate users, such as using resources and capabilities as authentication objects. +They should also expect to perform most storage and linking operations within transaction bodies +rather than inside contract utility functions. + +There are some scenarios where using an `AuthAccount` object is necessary, such as a cold storage multi-sig, +but those cases are extremely rare and `AuthAccount` usage should still be avoided unless absolutely necessary. + +## Auth references and capabilities should be avoided + +### Problem + +[Authorized references](https://cadence-lang.org/docs/language/references) allow downcasting restricted +types to their unrestricted type and should be avoided unless necessary. +The type that is being restricted could expose functionality that was not intended to be exposed. +If the `auth` keyword is used on local variables they will be references. +References are ephemeral and cannot be stored. +This prevents any reference casting to be stored under account storage. +Additionally, if the `auth` keyword is used to store a public capability, serious harm +could happen since the value could be downcasted to a type +that has functionality and values altered. + +### Example + +A commonly seen pattern in NFT smart contracts is including a public borrow function +that borrows an auth reference to an NFT (eg. [NBA Top Shot](https://github.com/dapperlabs/nba-smart-contracts/blob/95fe72b7e94f43c9eff28412ce3642b69dcd8cd5/contracts/TopShot.cdc#L889-L906)). +This allows anyone to access the stored metadata or extra fields that weren't part +of the NFT standard. While generally safe in most scenarios, not all NFTs are built the same. +Some NFTs may have privileged functions that shouldn't be exposed by this method, +so please be cautious and mindful when imitating NFT projects that use this pattern. + +### Another Example + +When we create a public capability for our `FungibleToken.Vault` we do not use an auth capability: + +```cadence +// GOOD: Create a public capability to the Vault that only exposes +// the balance field through the Balance interface +signer.link<&FlowToken.Vault{FungibleToken.Balance}>( + /public/flowTokenBalance, + target: /storage/flowTokenVault +) +``` + +If we were to use an authorized type for the capability, like so: + +```cadence +// BAD: Create an Authorized public capability to the Vault that only exposes +// the balance field through the Balance interface +// Authorized referenced can be downcasted to their unrestricted types, which is dangerous +signer.link( + /public/flowTokenBalance, + target: /storage/flowTokenVault +) +``` + +Then anyone in the network could take that restricted reference +that is only supposed to expose the balance field and downcast it to expose the withdraw field +and steal all our money! + +```cadence +// Exploit of the auth capability to expose withdraw +let balanceRef = getAccount(account) + .getCapability(/public/flowTokenBalance) + .borrow()! + +let fullVaultRef = balanceRef as! &FlowToken.Vault + +// Withdraw the newly exposed funds +let stolenFunds <- fullVaultRef.withdraw(amount: 1000000) +``` + +## Events from resources may not be unique + +### Problem + +Public functions in a contract can be called by anyone, e.g. any other contract or any transaction. +If that function creates a resource, and that resource has functions that emit events, +that means any account can create an instance of that resource and emit those events. +If those events are meant to indicate actions taken using a single instance of that resource +(eg. admin object, registry), or instances created through a particular workflow, +it's possible that events from other instances may be mixed in with the ones you're querying for - +making the event log search and management more cumbersome. + +### Solution + +To fix this, if there should be only a single instance of the resource, +it should be created and `link()`ed to a public path in an admin account's storage +during the contracts's initializer. + +# Access Control + +## Public functions and fields should be avoided + +### Problem + +Be sure to keep track of access modifiers when structuring your code, and make public only what should be public. +Accidentally exposed fields can be a security hole. + +### Solution + +When writing your smart contract, look at every field and function and make sure +that they are all `access(self)`, `access(contract)`, or `access(account)`, unless otherwise needed. + +## Capability-Typed public fields are a security hole + +This is a specific case of "Public Functions And Fields Should Be Avoided", above. + +### Problem + +The values of public fields can be copied. Capabilities are value types, +so if they are used as a public field, anyone can copy it from the field +and call the functions that it exposes. +This almost certainly is not what you want if a capability +has been stored as a field on a contract or resource in this way. + +### Solution + +For public access to a capability, place it in an accounts public area so this expectation is explicit. + +## Array or dictionary fields should be private + + + +This anti-pattern has been addressed with [FLIP #703](https://github.com/onflow/flips/blob/main/cadence/20211129-cadence-mutability-restrictions.md) + + + +### Problem + +This is a specific case of "Public Functions And Fields Should Be Avoided", above. +Public array or dictionary fields are not directly over-writable, +but their members can be accessed and overwritten if the field is public. +This could potentially result in security vulnerabilities for the contract +if these fields are mistakenly made public. + +Ex: + +```cadence +pub contract Array { + // array is intended to be initialized to something constant + pub let shouldBeConstantArray: [Int] +} +``` + +Anyone could use a transaction like this to modify it: + +```cadence +import Array from 0x01 + +transaction { + execute { + Array.shouldbeConstantArray[0] = 1000 + } +} +``` + +### Solution + +Make sure that any array or dictionary fields in contracts, structs, or resources +are `access(contract)` or `access(self)` unless they need to be intentionally made public. + +```cadence +pub contract Array { + // array is inteded to be initialized to something constant + access(self) let shouldBeConstantArray: [Int] +} +``` + +## Public admin resource creation functions are unsafe + +This is a specific case of "Public Functions And Fields Should Be Avoided", above. + +### Problem + +A public function on a contract that creates a resource can be called by any account. +If that resource provides access to admin functions then the creation function should not be public. + +### Solution + +To fix this, a single instance of that resource should be created in the contract's `init()` method, +and then a new creation function can be potentially included within the admin resource, if necessary. +The admin resource can then be `link()`ed to a private path in an admin's account storage during the contract's initializer. + +### Example + +```cadence +// Pseudo-code + +// BAD +pub contract Currency { + pub resource Admin { + pub fun mintTokens() + } + + // Anyone in the network can call this function + // And use the Admin resource to mint tokens + pub fun createAdmin(): @Admin { + return <-create Admin() + } +} + +// This contract makes the admin creation private and in the initializer +// so that only the one who controls the account can mint tokens +// GOOD +pub contract Currency { + pub resource Admin { + pub fun mintTokens() + + // Only an admin can create new Admins + pub fun createAdmin(): @Admin { + return <-create Admin() + } + } + + init() { + // Create a single admin resource + let firstAdmin <- create Admin() + + // Store it in private account storage in `init` so only the admin can use it + self.account.save(<-firstAdmin, to: /storage/currencyAdmin) + } +} +``` + +## Do not modify smart contract state or emit events in public struct initializers + +This is another example of the risks of having publicly accessible parts to your smart contract. + +### Problem + +Data structure definitions in Cadence currently must be declared as public so that they can be used by anyone. +Structs do not have the same restrictions that resources have on them, +which means that anyone can create a new instance of a struct without going through any authorization. + +### Solution + +Any contract state-modifying operations related to the creation of structs +should be contained in restricted resources instead of the initializers of structs. + +### Example + +This used to be a bug in the NBA Top Shot smart contract, so we'll use that as an example. +Before, when it created a new play, +[it would initialize the play record with a struct,](https://github.com/dapperlabs/nba-smart-contracts/blob/55645478594858a6830e4ab095034068ef9753e9/contracts/TopShot.cdc#L155-L158) +which increments the number that tracks the play IDs and emits an event: + +```cadence +// Simplified Code +// BAD +// +pub contract TopShot { + + // The Record that is used to track every unique play ID + pub var nextPlayID: UInt32 + + pub struct Play { + + pub let playID: UInt32 + + init() { + + self.playID = TopShot.nextPlayID + + // Increment the ID so that it isn't used again + TopShot.nextPlayID = TopShot.nextPlayID + 1 + + emit PlayCreated(id: self.playID, metadata: metadata) + } + } +} +``` + +This is a risk because anyone can create the `Play` struct as many times as they want, +which could increment the `nextPlayID` field to the max `UInt32` value, +effectively preventing new plays from being created. It also would emit bogus events. + +This bug was fixed by +[instead updating the contract state in the admin function](https://github.com/dapperlabs/nba-smart-contracts/blob/master/contracts/TopShot.cdc#L682-L685) +that creates the plays. + + +```cadence +// Update contract state in admin resource functions +// GOOD +// +pub contract TopShot { + + // The Record that is used to track every unique play ID + pub var nextPlayID: UInt32 + + pub struct Play { + + pub let playID: UInt32 + + init() { + self.playID = TopShot.nextPlayID + } + } + + pub resource Admin { + + // Protected within the private admin resource + pub fun createPlay() { + // Create the new Play + var newPlay = Play() + + // Increment the ID so that it isn't used again + TopShot.nextPlayID = TopShot.nextPlayID + UInt32(1) + + emit PlayCreated(id: newPlay.playID, metadata: metadata) + + // Store it in the contract storage + TopShot.playDatas[newPlay.playID] = newPlay + } + } +} +``` diff --git a/docs/build/best-practices/contract-upgrades.md b/docs/build/best-practices/contract-upgrades.md new file mode 100644 index 0000000000..20712b49de --- /dev/null +++ b/docs/build/best-practices/contract-upgrades.md @@ -0,0 +1,30 @@ +--- +title: Contract Upgrades with Incompatible Changes +sidebar_position: 4 +--- + +### Problem + +I have an incompatible upgrade for a contract. How can I deploy this? + +### Solution + +Please don't perform incompatible upgrades between contract versions in the same account. +There is too much that can go wrong. + +You can make [compatible upgrades](https://cadence-lang.org/docs/language/contract-updatability) and then run a post-upgrade function on the new contract code if needed. + +If you must replace your contract rather than update it, +the simplest solution is to add or increase a suffix on any named paths in the contract code +(e.g. `/public/MyProjectVault` becomes `/public/MyProjectVault002`) in addition to making the incompatible changes, +then create a new account and deploy the updated contract there. + +⚠️ Flow identifies types relative to addresses, so you will also need to provide _upgrade transactions_ to exchange the old contract's resources for the new contract's ones. Make sure to inform users as soon as possible when and how they will need to perform this task. + +If you absolutely must keep the old address when making an incompatible upgrade, then you do so at your own risk. Make sure you perform the following actions in this exact order: + +1. Delete any resources used in the contract account, e.g. an Admin resource. +2. Delete the contract from the account. +3. Deploy the new contract to the account. + +⚠️ Note that if any user accounts contain `structs` or `resources` from the _old_ version of the contract that have been replaced with incompatible versions in the new one, **they will not load and will cause transactions that attempt to access them to crash**. For this reason, once any users have received `structs` or `resources` from the contract, this method of making an incompatible upgrade should not be attempted! diff --git a/docs/build/best-practices/design-patterns.md b/docs/build/best-practices/design-patterns.md new file mode 100644 index 0000000000..39f00d7add --- /dev/null +++ b/docs/build/best-practices/design-patterns.md @@ -0,0 +1,494 @@ +--- +title: Cadence Design Patterns +sidebar_position: 1 +sidebar_label: Design Patterns +--- + +:::info +This is a selection of software design patterns developed by core Flow developers +while writing Cadence code for deployment to Flow Mainnet. + +Many of these design patters apply to most other programming languages, but some are specific to Cadence. + +[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are building blocks for software development. +They may provide a solution to a problem that you encounter when writing smart contracts in Cadence. +If they do not clearly fit, these patterns may not be the right solution for a given situation or problem. +They are not meant to be rules to be followed strictly, especially where a better solution presents itself. +::: + +# General + +These are general patterns to follow when writing smart contracts. + +## Use named value fields for constants instead of hard-coding + +### Problem + +Your contracts, resources, and scripts all have to refer to the same value. +A number, a string, a storage path, etc. +Entering these values manually in transactions and scripts is a potential source of error. +See [Wikipedia's page on magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) + +### Solution + +Add a public (`pub`), constant (`let`) field, e.g. a `Path` , to the contract responsible for the value, +and set it in the contract's initializer. +Refer to that value via this public field rather than specifying it manually. + +Example Snippet: + +```cadence + +// BAD Practice: Do not hard code storage paths +pub contract NamedFields { + pub resource Test {} + + init() { + // BAD: Hard-coded storage path + self.account.save(<-create Test(), to: /storage/testStorage) + } +} + +// GOOD practice: Instead, use a field +// +pub contract NamedFields { + pub resource Test {} + + // GOOD: field storage path + pub let TestStoragePath: StoragePath + + init() { + // assign and access the field here and in transactions + self.TestStoragePath = /storage/testStorage + self.account.save(<-create Test(), to: self.TestStoragePath) + } +} + +``` + +[Example Code](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L718) + +## Script-Accessible public field/function + +Data availability is important in a blockchain environment. +It is useful to publicize information about your smart contract and the assets it controls +so other smart contracts and apps can easily query it. + +### Problem + +Your contract, resource or struct has a field or resource that will need to be read and used on or off-chain, often in bulk. + +### Solution + +Make sure that the field can be accessed from a script (using a `PublicAccount`) +rather than requiring a transaction (using an `AuthAccount`). +This saves the time and fees required to read a property using a transaction. +Making the field or function `pub` and exposing it via a `/public/` capability will allow this. + +Be careful not to expose any data or functionality that should be kept private when doing so. + +Example: + +```cadence +// BAD: Field is private, so it cannot be read by the public +access(self) let totalSupply: UFix64 + +// GOOD: Field is public, so it can be read and used by anyone +pub let totalSupply: UFix64 +``` + +## Script-Accessible report + +### Problem + +Your contract has a resource that you wish to access fields of. +Resources are often stored in private places and are hard to access. +Additionally, scripts cannot return resources to the external context, +so a struct must be used to hold the data. + +### Solution + +Return a reference to a resource if the data from a single resource is all that is needed. +Otherwise, declare a struct to hold the data that you wish to return from the script. +Write a function that fills out the fields of this struct with the data +from the resource that you wish to access. +Then call this on the resource that you wish to access the fields of in a script, +and return the struct from the script. + +See [Script-Accessible public field/function](#script-accessible-public-fieldfunction), above, for how best to expose this capability. + +### Example Code + +```cadence +pub contract AContract { + pub let BResourceStoragePath: StoragePath + pub let BResourcePublicPath: PublicPath + + init() { + self.BResourceStoragePath = /storage/BResource + self.BResourcePublicPath = /public/BResource + } + + // Resource definition + pub resource BResource { + pub var c: UInt64 + pub var d: String + + + // Generate a struct with the same fields + // to return when a script wants to see the fields of the resource + // without having to return the actual resource + pub fun generateReport(): BReportStruct { + return BReportStruct(c: self.c, d: self.d) + } + + init(c: UInt64, d: String) { + self.c = c + self.d = d + } + } + + // Define a struct with the same fields as the resource + pub struct BReportStruct { + pub var c: UInt64 + pub var d: String + + init(c: UInt64, d: String) { + self.c = c + self.d = d + } + + } +} +... +// Transaction +import AContract from 0xAContract + +transaction { + prepare(acct: AuthAccount) { + //... + acct.link<&AContract.BResource>(AContract.BResourcePublicPath, target: AContract.BResourceStoragePath) + } +} +// Script +import AContract from 0xAContract + +// Return the struct with a script +pub fun main(account: Address): AContract.BReportStruct { + // borrow the resource + let b = getAccount(account) + .getCapability(AContract.BResourcePublicPath) + .borrow<&AContract.BResource>() + // return the struct + return b.generateReport() +} +``` + +## Init Singleton + +### Problem + +An admin resource must be created and delivered to a specified account. +There should not be a function to do this, as that would allow anyone to create an admin resource. + +### Solution + +Create any one-off resources in the contract's `init()` function +and deliver them to an address or `AuthAccount` specified as an argument. + +See how this is done in the LockedTokens contract init function: + +[LockedTokens.cdc](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L718) + +and in the transaction that is used to deploy it: + +[admin_deploy_contract.cdc](https://github.com/onflow/flow-core-contracts/blob/master/transactions/lockedTokens/admin/admin_deploy_contract.cdc) + + +## Use descriptive names for fields, paths, functions and variables + +### Problem + +Smart contracts often are vitally important pieces of a project and often have many other +smart contracts and applications that rely on them. +Therefore, they need to be clearly written and easy to understand. + +### Solution + +All fields, functions, types, variables, etc., need to have names that clearly describe what they are used for. + +`account` / `accounts` is better than `array` / `element`. + +`providerAccount` / `tokenRecipientAccount` is better than `acct1` / `acct2`. + +`/storage/bestPracticesDocsCollectionPath` is better than `/storage/collection` + +### Example +```cadence +// BAD: Unclear naming +// +pub contract Tax { + // Do not use abbreviations unless absolutely necessary + pub var pcnt: UFix64 + + // Not clear what the function is calculating or what the parameter should be + pub fun calculate(num: UFix64): UFix64 { + // What total is this referring to? + let total = num + (num * self.pcnt) + + return total + } +} + +// GOOD: Clear naming +// +pub contract TaxUtilities { + // Clearly states what the field is for + pub var taxPercentage: UFix64 + + // Clearly states that this function calculates the + // total cost after tax + pub fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { + let postTaxCost = preTaxCost + (preTaxCost * self.taxPercentage) + + return postTaxCost + } +} +``` + +## Include concrete types in type constraints, especially "Any" types + +### Problem + +When specifying type constraints for capabilities or borrows, concrete types often do not get specified, +making it unclear if the developer actually intended it to be unspecified or not. +Paths also use a shared namespace between contracts, so an account may have stored a different object +in a path that you would expect to be used for something else. +Therefore, it is important to be explicit when getting objects or references to resources. + + +### Solution + +A good example of when the code should specify the type being restricted is checking the FLOW balance: +The code must borrow `&FlowToken.Vault{FungibleToken.Balance}`, in order to ensure that it gets a FLOW token balance, +and not just `&{FungibleToken.Balance}`, any balance – the user could store another object +that conforms to the balance interface and return whatever value as the amount. + +When the developer does not care what the concrete type is, they should explicitly indicate that +by using `&AnyResource{Receiver}` instead of `&{Receiver}`. +In the latter case, `AnyResource` is implicit, but not as clear as the former case. + +## Plural names for arrays and maps are preferable + +e.g. `accounts` rather than `account` for an array of accounts. + +This signals that the field or variable is not scalar. +It also makes it easier to use the singular form for a variable name during iteration. + +## Use transaction post-conditions when applicable + +### Problem + +Transactions can contain any amount of valid Cadence code and access many contracts and accounts. +The power of resources and capabilities means that there may be some behaviors of programs that are not expected. + +### Solution + +It is usually safe to include post-conditions in transactions to verify the intended outcome. + +### Example + +This could be used when purchasing an NFT to verify that the NFT was deposited in your account's collection. + +```cadence +// Psuedo-code + +transaction { + + pub let buyerCollectionRef: &NonFungibleToken.Collection + + prepare(acct: AuthAccount) { + + // Get tokens to buy and a collection to deposit the bought NFT to + let temporaryVault <- vaultRef.withdraw(amount: 10.0) + let self.buyerCollectionRef = acct.borrow(from: /storage/Collection) + + // purchase, supplying the buyers collection reference + saleRef.purchase(tokenID: 1, recipient: self.buyerCollectionRef, buyTokens: <-temporaryVault) + + } + post { + // verify that the buyer now owns the NFT + self.buyerCollectionRef.idExists(1) == true: "Bought NFT ID was not deposited into the buyers collection" + } +} +``` + +## Avoid excessive load and save storage operations (prefer in-place mutations) + +### Problem + +When modifying data in account storage, `load()` and `save()` are costly operations. +This can quickly cause your transaction to reach the gas limit or slow down the network. + +This also applies to contract objects and their fields (which are implicitly stored in storage, i.e. read from/written to), +or nested resources. Loading them from their fields just to modify them and save them back +is just as costly. + +For example, a collection contains a dictionary of NFTs. There is no need to move the whole dictionary out of the field, +update the dictionary on the stack (e.g. adding or removing an NFT), +and then move the whole dictionary back to the field, it can be updated in-place. +The same goes for a more complex data structure like a dictionary of nested resources: +Each resource can be updated in-place by taking a reference to the nested object instead of loading and saving. + +### Solution + +For making modifications to values in storage or accessing stored objects, +`borrow()` should always be used to access them instead of `load` or `save` unless absolutely necessary. +`borrow()` returns a reference to the object at the storage path instead of having to load the entire object. +This reference can be assigned to or can be used to access fields or call methods on stored objects. + +### Example + +```cadence +// BAD: Loads and stores a resource to use it +// +transaction { + + prepare(acct: AuthAccount) { + + // Removes the vault from storage, a costly operation + let vault <- acct.load<@ExampleToken.Vault>(from: /storage/exampleToken) + + // Withdraws tokens + let burnVault <- vault.withdraw(amount: 10) + + destroy burnVault + + // Saves the used vault back to storage, another costly operation + acct.save(to: /storage/exampleToken) + + } +} + +// GOOD: Uses borrow instead to avoid costly operations +// +transaction { + + prepare(acct: AuthAccount) { + + // Borrows a reference to the stored vault, much less costly operation + let vault <- acct.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) + + let burnVault <- vault.withdraw(amount: 10) + + destroy burnVault + + // No `save` required because we only used a reference + } +} +``` + +# Capabilities + +## Capability Bootstrapping + +### Problem + +An account must be given a [capability](https://cadence-lang.org/docs/language/capabilities) +to a resource or contract in another account. To create, i.e. link the capability, +the transaction must be signed by a key which has access to the target account. + +To transfer / deliver the capability to the other account, the transaction also needs write access to that one. +It is not as easy to produce a single transaction which is authorized by two accounts +as it is to produce a typical transaction which is authorized by one account. + +This prevents a single transaction from fetching the capability +from one account and delivering it to the other. + +### Solution + +The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](https://cadence-lang.org/docs/language/accounts#account-inbox) + +Account A (which we will call the provider) creates the capability they wish to send to B (which we will call the recipient), +and stores this capability on their account in a place where the recipient can access it using the `Inbox.publish` function on their account. +They choose a name for the capability that the recipient can later use to identify it, and specify the recipient's address when calling `publish`. +This call to `publish` will emit an `InboxValuePublished` event that the recipient can listen for off-chain to know that the Capability is ready for them to claim. + +The recipient then later can use the `Inbox.claim` function to securely grab the capability from the provider's account. +They must provide the name and type with which the capability was published, as well as the address of the provider's account +(all of this information is available in the `InboxValuePublished` event emitted on `publish`). +This will remove the capability from the provider's account and emit an `InboxValueClaimed` event that the provider can listen for off-chain. + +One important caveat to this is that the published capability is stored on the provider's account until the recipient claims it, +so the provider can also use the `Inbox.unpublish` function to remove the capability from their account if they no longer wish to pay for storage for it. +This also requires the name and type which the capability was published, +and emits an `InboxValueUnpublished` event that the recipient can listen for off-chain. + +It is also important to note that the recipient becomes the owner of the capability object once they have claimed it, +and can thus store it or copy it anywhere they have access to. +This means providers should only publish capabilities to recipients they trust to use them properly, +or limit the type with which the capability is restricted in order to only give recipients access to the functionality +that the provider is willing to allow them to copy. + +## Capability Revocation + +### Problem + +A capability provided by one account to a second account must able to be revoked +by the first account without the co-operation of the second. + +See the [Capability Controller FLIP](https://github.com/onflow/flow/pull/798) for a proposal to improve this in the future. + +### Solution + +The first account should create the capability as a link to a capability in `/private/`, +which then links to a resource in `/storage/`. That second-order link is then handed +to the second account as the capability for them to use. + +**Account 1:** `/private/capability` → `/storage/resource` + +`/private/revokableLink` -> `/private/capability` + +**Account 2:** `/storage/capability -> (Capability(→Account 1: /private/revokableLink))` + +If the first account wants to revoke access to the resource in storage, +they should delete the `/private/` link that the second account's capability refers to. +Capabilities use paths rather than resource identifiers, so this will break the capability. + +The first account should be careful not to create another link at the same location +in its private storage once the capability has been revoked, +otherwise this will restore the second account's capability. + + +## Check for existing links before creating new ones + +When linking a capability, the link might be already present. +In that case, Cadence will not panic with a runtime error but the link function will return nil. +The documentation states that: The link function does not check if the target path is valid/exists +at the time the capability is created and does not check if the target value conforms to the given type. +In that sense, it is a good practice to check if the link does already exist with `AuthAccount.getLinkTarget` +before creating it with `AuthAccount.link()`. +`AuthAccount.getLinkTarget` will return nil if the link does not exist. + +### Example + +```cadence +transaction { + prepare(signer: AuthAccount) { + // Create a public capability to the Vault that only exposes + // the deposit function through the Receiver interface + // + // Check to see if there is a link already and unlink it if there is + + if signer.getLinkTarget(/public/exampleTokenReceiver) != nil { + signer.unlink(/public/exampleTokenReceiver) + } + + signer.link<&ExampleToken.Vault{FungibleToken.Receiver}>( + /public/exampleTokenReceiver, + target: /storage/exampleTokenVault + ) + } +} +``` diff --git a/docs/build/best-practices/project-development-tips.md b/docs/build/best-practices/project-development-tips.md new file mode 100644 index 0000000000..41ea604273 --- /dev/null +++ b/docs/build/best-practices/project-development-tips.md @@ -0,0 +1,302 @@ +--- +title: Flow Smart Contract Project Development Standards +sidebar_label: Development Standards +sidebar_position: 5 +description: "Learn how to effectively organize and manage a Cadence project" +--- + +# Smart Contract Project Development Standards + +## Context + +Smart Contracts are the bedrock piece of security for many important parts +of the Flow blockchain, as well as for any project that is deployed to a blockchain. + +They are also the most visible technical parts of any project, +since users will be querying them for data, building other smart contracts that interact with them, +and using them as learning materials and templates for future projects. +Furthermore, when deployed they are publicly available code on the blockchain +and often also in public Github repos. + +Therefore, the process around designing, building, testing, documenting, +and managing these projects needs to reflect the critical importance they hold in the ecosystem. + +Every software project strikes a balance between effort spent on product/feature delivery +vs the many other demands of the software development lifecycle, whether testing, technical debt, +automation, refactoring, or documentation etc. Building in Web3 we face the same trade-offs, +but in a higher risk and consequence environment than what is typical for most software. +A mismanaged or untested smart contract may result in **significant** financial losses +as a result of vulnerabilities which were overlooked then exploited. +We highly recommend builders adopt these best practices to help mitigate these risks. + +If they do so, they will be able to build better smart contracts, avoid potential bugs, +support user and third-party adoption of their projects, and increase their chances of success +by being a model for good software design. Additionally, the more projects that adopt +good software design and management standards normalizes this behavior, +encouraging other projects in the ecosystem to do the same which creates a healthier +and more vibrant community. + +Ensuring appropriate levels of testing results in better smart contracts which have +pro-actively modeled threats and engineered against them. Ensuring appropriate levels +of standards adoption ([FungibleToken](https://github.com/onflow/flow-ft), +[NFT Catalog](https://www.flow-nft-catalog.com/), [NFT StoreFront](https://github.com/onflow/nft-storefront), etc) by dapp +builders amplifies the network effects for all in the ecosystem. NFTs in one dapp can be +readily consumed by other dapps through on-chain events with no new integration +required. With your help and participation we can further accelerate healthy and vibrant +network effects across the Flow ecosystem! + +Some of these suggestions might seem somewhat unnecessary, +but it is important to model what a project can do to manage its smart contracts the best +so that hopefully all of the other projects follow suit. + +This also assumes standard software design best practices also apply. +Indeed, many of these suggestions are more general software design best practices, +but there may be others that are assumed but not included here. + +### Implementing These Practices + +This document serves as mostly an outline of best practices the projects should follow. +As with all best practices, teams will choose which applies to them and their work process, +however, we recommend that teams explicitly define a minimum acceptable set of standards +for themselves along with the mechanisms to ensure they are being observed. + +Some teams may also have their own set of development standards that achieve a similar goal +to these. These recommendations are not meant to be the only paths to success, +so if a team disagrees with some of these and wants to do things their own way, +they are welcome to pursue that. This document just shows some generic suggestions +for teams who might not know how they want to manage their project. + +## Design Process + +Smart contracts usually manage a lot of value, have many users, and are difficult to upgrade +for a variety of reasons. Therefore, it is important to have a clearly defined design +process for the smart contracts before much code is written so that the team +can set themselves up for success. + +Here are some recommendations for how projects can organize the foundations of their projects. + +### Projects should ensure that there is strong technical leadership for their smart contracts + +Developing a dapp requires a clear vision for the role of the smart contract and how it's integrated. +Security vulnerabilities may arise from bugs directly in smart contract code (and elsewhere in the system). +Asynchronous interaction vectors may lead to forms of malicious abuse, +DOS etc in a contract triggering explosive gas costs for the developer or other problems. + +We recommend that engineers leading a project and deploying to mainnet have an understanding +of software and security engineering fundamentals and have been thorough +in their Cadence skills development. More in-depth resources for learning Cadence +are available [here](https://cadence-lang.org/docs/). + +The technical leader should be someone who understands Cadence well and has written Cadence smart contracts +before. Production-level smart contracts are not the place for beginners to get their start. + +It should be this person’s responsibility to lead design discussions +with product managers and the community, write most of the code and tests, +solicit reviews, make requested changes and make sure the project gets completed in a timely manner. + +The leader should also understand how to sign transactions with the CLI +to deploy/upgrade smart contracts, run admin transactions, and troubleshoot problems, etc. +If something goes wrong in relation to the smart contract +that needs to be handled with a bespoke transaction, it is important that the owner +knows how to build and run transactions and scripts safely to address the issues +and/or upgrade the smart contracts. + +The project should also have a clear plan of succession in case the original owner +is not available or leaves the project. It is important that there are others who +can fill in who have a clear understanding of the code and requirements so they can give good feedback, +perform effective reviews, and make changes where needed. + +### Projects should maintain a well-organized open source Repo for their smart contracts + +As projects like NBA Topshot have shown, when a blockchain product becomes successful +others can and do to build on top of what you are doing. +Whether that is analytics, tools, or other value adds that could help grow your project ecosystem, +composability is key and that depends on open source development. +If there isn’t already an open source repo, builders should consider creating one. + +Builders can start from the [the Flow open source template](https://github.com/onflow/open-source-template) +and make sure all of their repo is set up with some initial documentation for what the repo is for +before any code is written. External developers and users should have an easily accessible home page +to go to to understand any given project. + +The repo should also have some sort of high-level design document that lays out +the intended design and architecture of the smart contract. +The project leads should determine what is best for them to include in the document, +but some useful things to include are basic user stories, architecture of the smart contracts, +and any questions that still need to be answered about it. + - Where applicable, diagrams should be made describing state machines, user flows, etc. + - This document should be shared in an issue in the open source repo + where the contracts or features are being developed, + then later moved to the README or another important docs page. + +A high level design is a key opportunity to model threats +and understand the risks of the system. The process of collaborating +and reviewing designs together helps ensure that more edge-cases are captured and addressed. +It's also a lot less effort to iterate on a design than on hundreds of lines of Cadence. + +## Development Process Recommendations + +### The Development process should be iterative, if possible + +The project should develop an MVP first, get reviews, and test thoroughly, +then add additional features with tests. This ensures that the core features are designed +thoughtfully and makes the review process easier because they can focus on each feature +one at a time instead of being overwhelmed by a huge block of code. + +### Comments and field/function descriptions are essential! + +Our experience writing many Cadence smart contracts has taught us how important documentation +is. It especially matters what is documented and for whom, and in that way we are no different from +any software language. The Why is super important, if for example something - an event - that +happens in one contract leads to outcomes in a different contract. The What helps give context, +the reason for the code turning out the way it is. The How, you don't document - you've written +the code. Comments should be directed to those who will follow after you in changing the code. + +Comments should be written at the same time (or even before) the code is written. +This helps the developer and reviewers understand the work-in-progress code better, +as well as the intentions of the design (for testing and reviewing). +Functions should be commented with a + - Description + - Parameter descriptions + - Return value descriptions + + +Top Level comments and comments for types, fields, events, +and functions should use `///` (three slashes) to be recognised by the +[Cadence Documentation Generator](https://github.com/onflow/cadence-tools/tree/master/docgen). +Regular comments within functions should only use two slashes (`//`) + +## Testing Recommendations + +Summarized below is a list of testing related recommendations +which are noteworthy to mention for a typical smart contract project. + +Popular testing frameworks to use for cadence are listed here: +- Javascript: [Flow JS Testing](../../tools/flow-js-testing/index.md) +- Go: [Overflow](https://github.com/bjartek/overflow) +- Cadence: [Cadence Testing Framework](../guides/smart-contracts/testing.md) +Tests written in Cadence! + +The same person who writes the code should also write the tests. +They have the clearest understanding of the code paths and edge cases. + + +Tests should be **mandatory**, not optional, even if the contract is copied from somewhere else. +There should be thorough emulator unit tests in the public repo. +[See the flow fungible token repo](https://github.com/onflow/flow-ft/tree/master/lib/js/test) +for an example of unit tests in javascript. + + +Every time there is a new Cadence version or emulator version, +the dependencies of the repo should be updated to make sure the tests are all still passing. + + +Tests should avoid being monolithic; +Individual test cases should be set up for each part of the contract to test them in isolation. +See the [`FlowEpoch` smart contract tests](https://github.com/onflow/flow-core-contracts/blob/master/lib/go/test/flow_epoch_test.go) +for examples written in Go where test cases are split +into separate blocks for different features. +There are some exceptions, like contracts that have to run through a state machine +to test different cases. Positive and negative cases need to be tested. + +Integration tests should also be written to ensure that your app and/or backend can interact +properly with the smart contracts. + +## Managing Project Keys and Deployments + +Smart contract keys and deployments are very important and need to be treated as such. + +### Private Keys should be stored securely + +Private Keys for the contract and/or admin accounts should not be kept in plain text format anywhere. +Projects should determine a secure solution that works best for them to store their private keys. +We recommend storing them in a secure key store such as google KMS or something similar. + +### Deployments to Testnet or Mainnet should be handled transparently + +As projects become more successful, communities around them grow. +In a trustless ecosystem, that also means more of others building on your contracts. +Before deploying or upgrading a contract, it is important to maintain +clear community communications with sufficient notice, since changes will always bring added risk. +Giving community members time to review and address issues with upgrades +before they happen builds trust and confidence in projects. +Here are a few suggestions for how to manage a deployment or upgrade. + +- Communicate to all stake-holders well in advance + - Share the proposal with the community at least a week in advance (unless it is a critical bug fix) + - Examples of places to share are your project's chat, forum, blog, email list, etc. + - This will allow the community and other stakeholders to have plenty of time + to view the upcoming changes and provide feedback if necessary. + - Share the time of the deployment and the deployment transaction with branch/commit hash information to ensure the transaction itself is correct. + - Coordinate deployment with stakeholders to make sure it is done correctly and on time. + +## Responsibilities to the Community + +Web3 brings tremendous possibilities for engineering applications with trustlessness +and composability in mind, with Cadence and Flow offering unique features to achieve this. +If every project treats their community and the Flow community with respect and care, +the things we can all build together will be very powerful. + +### Projects should have thorough documentation + +Encouraging adoption of project contracts to the broader ecosystem +raises the bar around code providing clear high-level descriptions, +with detailed and useful comments within contracts, transactions, and scripts. +The more that a project can be understood, that it adheres to standards, +and can be built upon with ease, the more likely others will build against it in turn. + +Each project should have a detailed README.md with these sections: + - Explanation of the project itself with links to the app + - Addresses on various networks + - High-level technical description of the contracts with emphasis on important types and functionality + - Architecture diagram (if applicable) + - Include links to tutorials if they are external + - Flow smart contract standards that a project implements + +Additionally, each contract, transaction, and script should have high-level descriptions +at the top of their files. This way, anyone in the community can easily +come in and understand what each one is doing without having to parse confusing code. + +### Projects should engage with and respond to their own Community + +Once a contract is deployed, the work doesn’t stop there. +Project communities require ongoing nurturing and support. +As the developer of a public project on a public blockchain, +the owners have an obligation to be helpful and responsive to the community +so that they can encourage composability and third party interactions. + +- Keep issues open in the repo. +- The owner should turn on email notifications for new issue creation in the repo. +- Respond to issues quickly and clean up unimportant ones. +- Consider blog posts to share more details on technical aspects of the project and upcoming changes. + +### Projects should contribute to the greater Flow and Cadence community + +Flow has a vibrant and growing community of contributors around the world. +Through our mutual collaboration we've had numerous community Flow Improvement Proposals +([FLIP](https://github.com/onflow/flow/tree/master/flips)s) shipped. +If you have an interest in a particular improvement for Flow or Cadence, +we host open meetings which you are welcome to join (announced on discord) +and can participate anytime on any of the FLIPs +[already proposed](https://github.com/onflow/flow/pulls?q=is%3Aopen+is%3Apr+label%3AFLIP). + +Responsible project maintainers should contribute to discussions +about important proposals (new cadence features, standard smart contracts, metadata, etc) +and generally be aware about evolving best practices and anti-pattern understandings. +Projects who contribute to these discussions are able to influence them to ensure +that the language/protocol changes are favorable to them +and the rest of the app developers in the ecosystem. +It also helps the owner to promote the project and themselves. + +Resources for Best Practices: + +- [cadence/design-pattern](./design-patterns.md) +- [cadence/anti-patterns](./anti-patterns.md) +- [cadence/security-best-practices](./security-best-practices.md) + +Composability and extensibility should also be priorities while designing, developing, +and documenting their projects. (Documentation for these topics coming soon) + + + +If you have any feedback about these guidelines, please create an issue in the onflow/cadence-style-guide repo or make a PR updating the guidelines so we can start a discussion. diff --git a/docs/build/best-practices/security-best-practices.md b/docs/build/best-practices/security-best-practices.md new file mode 100644 index 0000000000..328eb030c3 --- /dev/null +++ b/docs/build/best-practices/security-best-practices.md @@ -0,0 +1,65 @@ +--- +title: Cadence Security Best Practices +sidebar_label: Security Best Practices +sidebar_position: 3 +--- + +This is an opinionated list of best practices Cadence developers should follow to write more secure Cadence code. + +Some practices listed below might overlap with advice in the [Cadence Anti-Patterns](./design-patterns.md) section, which is a recommended read as well. + +## References + +[References](https://cadence-lang.org/docs/language/references) are ephemeral values and cannot be stored. If persistence is required, store a capability and borrow it when needed. + +Authorized references (references with the `auth` keyword) allow downcasting, e.g. a restricted type to its unrestricted type and should only be used in some specific cases. + +When exposing functionality, provide the least access necessary. Do not use authorized references, as they can be downcasted, potentially allowing a user to gain access to supposedly restricted functionality. For example, the fungible token standard provides an interface to get the balance of a vault, without exposing the withdrawal functionality. + +Be aware that the subtype or unrestricted type could expose functionality that was not intended to be exposed. Do not use authorized references when exposing functionality. For example, the fungible token standard provides an interface to get the balance of a vault, without exposing the withdrawal functionality. + +## Account Storage + +Don't trust a users’ [account storage](https://cadence-lang.org/docs/language/accounts#account-storage). Users have full control over their data and may reorganize it as they see fit. Users may store values in any path, so paths may store values of “unexpected” types. These values may be instances of types in contracts that the user deployed. + +Always [borrow](https://cadence-lang.org/docs/language/capabilities) with the specific type that is expected. Or, check if the value is an instance of the expected type. + +## Auth Accounts + +Access to an `AuthAccount` gives full access to the account's storage, keys, and contracts. Therefore, [avoid using AuthAccount](./anti-patterns.md#avoid-using-authaccount-as-a-function-parameter) as a function parameter unless absolutely necessary. + +It is preferable to use capabilities over direct `AuthAccount` storage when exposing account data. Using capabilities allows the revocation of access by unlinking and limits the access to a single value with a certain set of functionality – access to an `AuthAccount` gives full access to the whole storage, as well as key and contract management. + +## Capabilities + +Don’t store anything under the [public capability storage](https://cadence-lang.org/docs/language/capabilities) unless strictly required. Anyone can access your public capability using `AuthAccount.getCapability`. If something needs to be stored under `/public/`, make sure only read functionality is provided by restricting its type using either a resource interface or struct interface. + +When linking a capability, the link might already be present. In that case, Cadence will not panic with a runtime error but the link function will return `nil`. + +It is a good practice to check if the link already exists with `getLinkTarget` before creating it. This function will return `nil` if the link does not exist. + +If it is necessary to handle the case where borrowing a capability might fail, the `account.check` function can be used to verify that the target exists and has a valid type. + +Ensure capabilities cannot be accessed by unauthorized parties. For example, capabilities should not be accessible through a public field, including public dictionaries or arrays. Exposing a capability in such a way allows anyone to borrow it and perform all actions that the capability allows. + +## Transactions + +Audits of Cadence code should also include [transactions](https://cadence-lang.org/docs/language/transactions), as they may contain arbitrary code, just, like in contracts. In addition, they are given full access to the accounts of the transaction’s signers, i.e. the transaction is allowed to manipulate the signers’ account storage, contracts, and keys. + +Signing a transaction gives access to the `AuthAccount`, i.e. full access to the account’s storage, keys, and contracts. + +Do not blindly sign a transaction. The transaction could for example change deployed contracts by upgrading them with malicious statements, revoking or adding keys, transferring resources from storage, etc. + +## Types + +Use [restricted types and interfaces](https://cadence-lang.org/docs/language/restricted-types). Always use the most specific type possible, following the principle of least privilege. Types should always be as restrictive as possible, especially for resource types. + +If given a less-specific type, cast to the more specific type that is expected. For example, when implementing the fungible token standard, a user may deposit any fungible token, so the implementation should cast to the expected concrete fungible token type. + +## Access Control + +Declaring a field as [`pub/access(all)`](https://cadence-lang.org/docs/language/access-control) only protects from replacing the field’s value, but the value itself can still be mutated if it is mutable. Remember that containers, like dictionaries, and arrays, are mutable. + +Prefer non-public access to a mutable state. That state may also be nested. For example, a child may still be mutated even if its parent exposes it through a field with non-settable access. + +Do not use the `pub/access(all)` modifier on fields and functions unless necessary. Prefer `priv/access(self)`, or `access(contract)` and `access(account)` when other types in the contract or account need to have access. diff --git a/docs/build/building-vs-other-chains.md b/docs/build/building-vs-other-chains.md index 694545ec1a..d3284460a4 100644 --- a/docs/build/building-vs-other-chains.md +++ b/docs/build/building-vs-other-chains.md @@ -148,7 +148,7 @@ Access Nodes are the node type that are most useful for developers, as they prov - gRPC - Mainnet: `access.mainnet.nodes.onflow.org:9000` - Testnet: `access.devnet.nodes.onflow.org:9000` - - [Specification](../references/run-and-secure/nodes/access-api.mdx) + - [Specification](../references/run-and-secure/nodes/access-api.md) ### Running Your Own Node diff --git a/docs/build/cadence-reference/_category_.yml b/docs/build/cadence-reference/_category_.yml new file mode 100644 index 0000000000..e50ac98c5b --- /dev/null +++ b/docs/build/cadence-reference/_category_.yml @@ -0,0 +1 @@ +label: Cadence Reference \ No newline at end of file diff --git a/docs/build/cadence-reference/json-cadence-spec.md b/docs/build/cadence-reference/json-cadence-spec.md new file mode 100644 index 0000000000..eaaa1c7152 --- /dev/null +++ b/docs/build/cadence-reference/json-cadence-spec.md @@ -0,0 +1,869 @@ +--- +title: JSON-Cadence Data Interchange Format +sidebar_label: JSON-Cadence format +--- + +> Version 0.3.1 + +JSON-Cadence is a data interchange format used to represent Cadence values as language-independent JSON objects. + +This format includes less type information than a complete [ABI](https://en.wikipedia.org/wiki/Application_binary_interface), and instead promotes the following tenets: + +- **Human-readability** - JSON-Cadence is easy to read and comprehend, which speeds up development and debugging. +- **Compatibility** - JSON is a common format with built-in support in most high-level programming languages, making it easy to parse on a variety of platforms. +- **Portability** - JSON-Cadence is self-describing and thus can be transported and decoded without accompanying type definitions (i.e. an ABI). + +# Values + +--- + +## Void + +```json +{ + "type": "Void" +} +``` + +### Example + +```json +{ + "type": "Void" +} +``` + +--- + +## Optional + +```json +{ + "type": "Optional", + "value": null | +} +``` + +### Example + +```json +// Non-nil + +{ + "type": "Optional", + "value": { + "type": "UInt8", + "value": "123" + } +} + +// Nil + +{ + "type": "Optional", + "value": null +} +``` + +--- + +## Bool + +```json +{ + "type": "Bool", + "value": true | false +} +``` + +### Example + +```json +{ + "type": "Bool", + "value": true +} +``` + +--- + +## String + +```json +{ + "type": "String", + "value": "..." +} + +``` + +### Example + +```json +{ + "type": "String", + "value": "Hello, world!" +} +``` + +--- + +## Address + +```json +{ + "type": "Address", + "value": "0x0" // as hex-encoded string with 0x prefix +} +``` + +### Example + +```json +{ + "type": "Address", + "value": "0x1234" +} +``` + +--- + +## Integers + +`[U]Int`, `[U]Int8`, `[U]Int16`, `[U]Int32`,`[U]Int64`,`[U]Int128`, `[U]Int256`, `Word8`, `Word16`, `Word32`, or `Word64` + +Although JSON supports integer literals up to 64 bits, all integer types are encoded as strings for consistency. + +While the static type is not strictly required for decoding, it is provided to inform client of potential range. + +```json +{ + "type": "", + "value": "" +} +``` + +### Example + +```json +{ + "type": "UInt8", + "value": "123" +} +``` + +--- + +## Fixed Point Numbers + +`[U]Fix64` + +Although fixed point numbers are implemented as integers, JSON-Cadence uses a decimal string representation for readability. + +```json +{ + "type": "[U]Fix64", + "value": "." +} +``` + +### Example + +```json +{ + "type": "Fix64", + "value": "12.3" +} +``` + +--- + +## Array + +```json +{ + "type": "Array", + "value": [ + , + + // ... + ] +} +``` + +### Example + +```json +{ + "type": "Array", + "value": [ + { + "type": "Int16", + "value": "123" + }, + { + "type": "String", + "value": "test" + }, + { + "type": "Bool", + "value": true + } + ] +} +``` + +--- + +## Dictionary + +Dictionaries are encoded as a list of key-value pairs to preserve the deterministic ordering implemented by Cadence. + +```json +{ + "type": "Dictionary", + "value": [ + { + "key": "", + "value": + }, + ... + ] +} +``` + +### Example + +```json +{ + "type": "Dictionary", + "value": [ + { + "key": { + "type": "UInt8", + "value": "123" + }, + "value": { + "type": "String", + "value": "test" + } + } + ], + // ... +} +``` + +--- + +## Composites (Struct, Resource, Event, Contract, Enum) + +Composite fields are encoded as a list of name-value pairs in the order in which they appear in the composite type declaration. + +```json +{ + "type": "Struct" | "Resource" | "Event" | "Contract" | "Enum", + "value": { + "id": "", + "fields": [ + { + "name": "", + "value": + }, + // ... + ] + } +} +``` + +### Example + +```json +{ + "type": "Resource", + "value": { + "id": "0x3.GreatContract.GreatNFT", + "fields": [ + { + "name": "power", + "value": {"type": "Int", "value": "1"} + } + ] + } +} +``` + +--- + +## Path + +```json +{ + "type": "Path", + "value": { + "domain": "storage" | "private" | "public", + "identifier": "..." + } +} +``` + +### Example + +```json +{ + "type": "Path", + "value": { + "domain": "storage", + "identifier": "flowTokenVault" + } +} +``` + +--- + +## Type Value + +```json +{ + "type": "Type", + "value": { + "staticType": + } +} +``` + +### Example + +```json +{ + "type": "Type", + "value": { + "staticType": { + "kind": "Int", + } + } +} +``` + +--- + +## Capability + +```json +{ + "type": "Capability", + "value": { + "path": , + "address": "0x0", // as hex-encoded string with 0x prefix + "borrowType": , + } +} +``` + +### Example + +```json +{ + "type": "Capability", + "value": { + "path": { + "type": "Path", + "value": { + "domain": "public", + "identifier": "someInteger" + } + }, + "address": "0x1", + "borrowType": { + "kind": "Int" + } + } +} +``` + +--- + +## Functions + +```json +{ + "type": "Function", + "value": { + "functionType": + } +} +``` + +Function values can only be exported, they cannot be imported. + +### Example + +```json +{ + "type": "Function", + "value": { + "functionType": { + "kind": "Function", + "typeID": "(():Void)", + "parameters": [], + "return": { + "kind": "Void" + } + } + } +} +``` + +--- + +# Types + +## Simple Types + +These are basic types like `Int`, `String`, or `StoragePath`. + +```json +{ + "kind": "Any" | "AnyStruct" | "AnyResource" | "AnyStructAttachment" | "AnyResourceAttachment" | "Type" | + "Void" | "Never" | "Bool" | "String" | "Character" | + "Bytes" | "Address" | "Number" | "SignedNumber" | + "Integer" | "SignedInteger" | "FixedPoint" | + "SignedFixedPoint" | "Int" | "Int8" | "Int16" | + "Int32" | "Int64" | "Int128" | "Int256" | "UInt" | + "UInt8" | "UInt16" | "UInt32" | "UInt64" | "UInt128" | + "UInt256" | "Word8" | "Word16" | "Word32" | "Word64" | + "Fix64" | "UFix64" | "Path" | "CapabilityPath" | "StoragePath" | + "PublicPath" | "PrivatePath" | "AuthAccount" | "PublicAccount" | + "AuthAccount.Keys" | "PublicAccount.Keys" | "AuthAccount.Contracts" | + "PublicAccount.Contracts" | "DeployedContract" | "AccountKey" | "Block" +} +``` + +### Example + +```json +{ + "kind": "UInt8" +} +``` + +--- + +## Optional Types + +```json +{ + "kind": "Optional", + "type": +} +``` + +### Example + +```json +{ + "kind": "Optional", + "type": { + "kind": "String" + } +} +``` + +--- + +## Variable Sized Array Types + +```json +{ + "kind": "VariableSizedArray", + "type": +} +``` + +### Example + +```json +{ + "kind": "VariableSizedArray", + "type": { + "kind": "String" + } +} +``` + +--- + +## Constant Sized Array Types + +```json +{ + "kind": "ConstantSizedArray", + "type": , + "size": , +} +``` + +### Example + +```json +{ + "kind": "ConstantSizedArray", + "type": { + "kind": "String" + }, + "size":3 +} +``` + +--- + +## Dictionary Types + +```json +{ + "kind": "Dictionary", + "key": , + "value": +} +``` + +### Example + +```json +{ + "kind": "Dictionary", + "key": { + "kind": "String" + }, + "value": { + "kind": "UInt16" + }, +} +``` + +--- + +## Composite Types + +```json +{ + "kind": "Struct" | "Resource" | "Event" | "Contract" | "StructInterface" | "ResourceInterface" | "ContractInterface", + "type": "", // this field exists only to keep parity with the enum structure below; the value must be the empty string + "typeID": "", + "initializers": [ + , + + // ... + ], + "fields": [ + , + + // ... + ], +} +``` + +### Example + +```json +{ + "kind": "Resource", + "type": "", + "typeID": "0x3.GreatContract.GreatNFT", + "initializers":[ + [ + { + "label": "foo", + "id": "bar", + "type": { + "kind": "String" + } + } + ] + ], + "fields": [ + { + "id": "foo", + "type": { + "kind": "String" + } + } + ] +} +``` + +--- + +## Field Types + +```json +{ + "id": "", + "type": +} +``` + +### Example + +```json +{ + "id": "foo", + "type": { + "kind": "String" + } +} +``` + +--- + +## Parameter Types + +```json +{ + "label": "