diff --git a/docs/anti-patterns.mdx b/docs/anti-patterns.mdx index dca729cda4..a96a6cc10b 100644 --- a/docs/anti-patterns.mdx +++ b/docs/anti-patterns.mdx @@ -4,35 +4,197 @@ title: Cadence Anti-Patterns This is an opinionated list of issues that can be improved if they are found in Cadence code intended for production. -## Security and Robustness +# Security and Robustness -### Events From Resources Might Not Be Unique +## Avoid using `AuthAccount` as a function parameter -A public function on a contract that creates a resource can be called by any account. +### 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(<-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 -If that resource has functions that emit events, any account can create an instance of that resource and emit those events. +[Authorized references](/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. -If those events are meant to indicate actions taken using a single instance of that resource (for example an admin object, switchboard, or registry), or instances created through a particular workflow, then this will make event log search and management harder because events from other instances may be mixed in with the ones that you wish to examine. +### Example -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. +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. -### Named Value Fields Are Preferable +### Another Example -Where contracts, resources, and scripts all have to repeatedly refer to the same constant values, entering these manually is a potential source of error. These are known as [magic values](). +When we create a public capability for our `FungibleToken.Vault` we do not use an auth capability: -Rather than typing the values manually in functions, transactions and scripts, store the values once in fields of the contract responsible and then access them via those fields. This is the [Named Value Field pattern](design-patterns/#named-value-field) +```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 solenFunds <- 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. -### Public Functions and Fields Should Be Avoided +# 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 -### Array or Dictionary Fields Should Be Private +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. -#### Problem +## Capability-Typed public fields are a security hole 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. +### 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/flow/blob/master/flips/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: @@ -50,14 +212,15 @@ import Array from 0x01 transaction { execute { - Array.shouldbeConstantArray[0] = 1000 + Array.shouldbeConstantArray[0] = 1000 } } ``` -#### Solution +### 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. +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 { @@ -66,52 +229,153 @@ pub contract Array { } ``` -### Public Admin Resource Creation Functions Are Unsafe +## 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. -If that resource provides access to admin functions then the function should not be public. +### Solution -To fix this, a single instance of that resource created using it then `link()`ed to a private path in an admin account's storage during the contract's initializer. If the code to create the resource is complex enough that it needs its own function, the function that creates the resource should use `access(contract)` as its access modifier. +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. -### Public Capability Fields Are A Security Hole +### Example -This is a specific case of "Public Functions And Fields Should Be Avoided", above. +```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 values of public fields can be copied. Capabilities are value types. Anyone who receives a Capability can use it. So if they can copy it from a public field they can call the functions that it exposes. + // The Record that is used to track every unique play ID + pub var nextPlayID: UInt32 -This almost certainly is not what you want if a capability has been stored in this way. For public access to a capability, place it in an accounts public area. + pub struct Play { -### Users Might Rebind Restricted Public Capability Paths + pub let playID: UInt32 -A public capability on a user account that is a variant of a standard resource (such as a Vault) that has additional logic implemented to restrict or alter its behaviour can be replaced by that user at the same path with another resource that does not have those alterations. + init() { -To fix this, make sure to carefully specify the type of the capability that you are expecting in any code that accesses it. + self.playID = TopShot.nextPlayID -## Readability + // Increment the ID so that it isn't used again + TopShot.nextPlayID = TopShot.nextPlayID + 1 -### Descriptive Names Are Preferable + emit PlayCreated(id: self.playID, metadata: metadata) + } + } +} +``` -`account` / `accounts` is better than `array` / `element`. +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. -`providerAccount` / `tokenRecipientAccount` is better than `acct1` / `acct2`. +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. -Unless of course you are dealing with entities that really are best named that way, for example in utility code. -### Omitting "Any" Types In Constraints Is Preferable +```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 -Prefer `&{Receiver}` to `&AnyResource{Receiver}`. + pub struct Play { -It's clearer and the `Any...` is implicit. + pub let playID: UInt32 -Note that this doesn't mean that the restricted type should _always_ be emitted, only if it's specifically `AnyStruct` or `AnyResource` . + init() { + self.playID = TopShot.nextPlayID + } + } -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. + pub resource Admin { -### Plural Names For Arrays And Maps Are Preferable + // Protected within the private admin resource + pub fun createPlay() { + // Create the new Play + var newPlay = Play() -e.g. `accounts` rather than `account` for an array of accounts. + // 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 + } + } +} +``` -This signals that the field or variable is not scalar. It also makes it easier to the singular form for a variable name during iteration. diff --git a/docs/design-patterns.mdx b/docs/design-patterns.mdx index 6e5b102320..b0ac0ce359 100644 --- a/docs/design-patterns.mdx +++ b/docs/design-patterns.mdx @@ -5,66 +5,171 @@ title: Cadence Design Patterns 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. -## Capability Receiver +# General + +These are general patterns to follow when writing smart contracts. + +## Use named value fields for constants instead of hard-coding ### Problem -An account must be given a [capability](/cadence/language/capability-based-access-control/) to a resource or contract in another account but a single transaction with both accounts authorizing it cannot be easily produced. This prevents a single transaction from fetching the capability from one account and delivering it to the other. +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 -Account B creates a resource that can receive the capability and stores this in their `/storage/` area. They then expose a Capability to this in their `/public/` area with a function that can receive the desired Capability and store it in the resource for later use. +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. -Account A fetches the receiver Capability from B's `/public/` area, creates the desired Capability, and passes it to the receiver function. The receiver function stores the Capability in the resource that it is on in account B's `/storage/` area for later use. +Example Snippet: -There are two nuances to this workflow that are required to ensure that it is secure. +```cadence -The first is that only Account A should be able to create instances of the desired Capability. This ensures that nobody else can create instances of it and call the receiver function on B's receiver capability instead of A. This means that A is probably an admin account. +// BAD Practice: Do not hard code storage paths +pub contract NamedFields { + pub resource Test {} -The second is that the field on the receiver resource that stores the desired Capability should be access(contract) and only accessed by code within the contract that needs to. This ensures that B cannot copy and share the Capability with anyone else. + init() { + // BAD: Hard-coded storage path + self.account.save(<-create Test(), to: /storage/testStorage) + } +} -### Example Code +// GOOD practice: Instead, use a field +// +pub contract NamedFields { + pub resource Test {} -See: + // GOOD: field storage path + pub let TestStoragePath: StoragePath -[LockedTokens.cdc](https://github.com/onflow/flow-core-contracts/blob/bfb115869bd9f815cde1fe64ab6d91ca95c0938e/contracts/LockedTokens.cdc#L527) + init() { + // assign and access the field here and in transactions + self.TestStoragePath = /storage/testStorage + self.account.save(<-create Test(), to: self.TestStoragePath) + } +} -[custody_setup_account_creator.cdc](https://github.com/onflow/flow-core-contracts/blob/79941fe65b634800065a440ae5243744b2ca8a2f/transactions/lockedTokens/admin/custody_setup_account_creator.cdc) +``` -[admin_deposit_account_creator.cdc](https://github.com/onflow/flow-core-contracts/blob/79941fe65b634800065a440ae5243744b2ca8a2f/transactions/lockedTokens/admin/admin_deposit_account_creator.cdc) +[Example Code](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L583) -## Capability Revocation +## 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 -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. +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 -The first account should create the capability as a link to a capability in `/private/`, which then links to a resource in `/storage/`. That first link is then handed to the second account as the capability for them to use. This can be stored in their private storage or a Capability Receiver. +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. -**Account 1:** `/private/capability` → `/storage/resource` +Be careful not to expose any data or functionality that should be kept private when doing so. -**Account 2:** `Capability Receiver(Capability(→Account 1: /private/capability))` +Example: -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. +```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 -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. +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 construct 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](#script-accessible-public-field), above, for how best to expose this capability. + +### Example Code + +```cadence +pub contract AContract { + + // 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: c, d: 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 + } + + } +} +... +import A from 0xA + +// Return the struct with a script +pub fun main(): A.BReport { + let b: AContract.BResource // Borrow the resource + let report = b.generateReport() + return b +} +``` ## 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. +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. +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: @@ -74,75 +179,297 @@ 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) -## Named Value Field + +## Use desciptive names for fields, paths, functions, and variables ### Problem -Your contracts, resources, and scripts all have to refer to the same value. A number, a string, a storage path. Entering these values manually in transactions and scripts is a potential source of error. See: [https://en.wikipedia.org/wiki/Magic*number*(programming)]() +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 writted and easy to understand. ### Solution -Add an `access(all)` field, e.g. a `Path` , to the contract responsible for the value, and set it in the contract's initializer. Then refer to that value via this public field rather than specifying it manually. +All fields, functions, types, variables, etc need to have names that clearly describe what they are used for. -[Example Code](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L583) +`account` / `accounts` is better than `array` / `element`. + +`providerAccount` / `tokenRecipientAccount` is better than `acct1` / `acct2`. -## Script-Accessible Public Field +`/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 -Your contract, resource or struct has a field that will need to be read and used off-chain, often in bulk. +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 -Make sure that the field can be accessed from a script (using an `Account`) rather than requiring a transaction (using an `AuthAccount`). This saves the time and soon the expense required by having to read a property using a transaction. Making the field `access(all)` 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. +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. -## Script-Accessible Report +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 -Your contract, resource or struct has a resource that you wish to access fields of off-chain via a script. But scripts cannot return resources. +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 -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. +It is usually safe to include post-conditions in transactions to verify the intended outcome. -See [Script-Accessible Public Field](#script-accessible-public-field), above, for how best to expose this capability. +### Example -### Example Code +This could be used when purchasing an NFT to verify that the NFT was deposited in your account's collection. ```cadence -pub contract AContract { - pub struct BReportStruct { - pub var c: UInt64 - pub var d: string +// Psuedo-code - init(c: UInt64, d: string) { - self.c = c - self.d = d - } +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" + } +} +``` - pub resource BResource { - pub var c: UInt64 - pub var d: string +## Avoid excessive load and save storage operations (prefer in-place mutations) - pub fun generateReport() BReportStruct { - return BReportStruct(c: c, d: d) - } +### 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) - init(c: UInt64, d: string) { - self.c = c - self.d = d - } } } -... -import A from 0xA -pub fun main(): A.BReport { - let b: AContract.BResource // Borrow the resource - let report = b.generateReport() - return b +// 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 Receiver + +### Problem + +An account must be given a [capability](/cadence/language/capability-based-access-control/) +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 + +Account B creates a resource that can receive the capability and stores this in their `/storage/` area. +They then expose a Capability to this in their `/public/` area with a function +that can receive the desired Capability and store it in the resource for later use. + +Account A fetches the receiver Capability from B's `/public/` area, creates the desired Capability, +and passes it to the receiver function. The receiver function stores the Capability +in the resource that it is on in account B's `/storage/` area for later use. + +There are two nuances to this workflow that are required to ensure that it is secure. + +The first is that only Account A should be able to create instances of the desired Capability. +This ensures that nobody else can create instances of it and call the receiver function +on B's receiver capability instead of A. This means that A is probably an admin account. + +The second is that the field on the receiver resource that stores the desired Capability +should be `access(contract)` and only accessed by code within the contract that needs to. +This ensures that B cannot copy and share the Capability with anyone else. + +### Example Code + +See: + +[LockedTokens.cdc](https://github.com/onflow/flow-core-contracts/blob/bfb115869bd9f815cde1fe64ab6d91ca95c0938e/contracts/LockedTokens.cdc#L527-L546) + +[custody_setup_account_creator.cdc](https://github.com/onflow/flow-core-contracts/blob/79941fe65b634800065a440ae5243744b2ca8a2f/transactions/lockedTokens/admin/custody_setup_account_creator.cdc) + +[admin_deposit_account_creator.cdc](https://github.com/onflow/flow-core-contracts/blob/79941fe65b634800065a440ae5243744b2ca8a2f/transactions/lockedTokens/admin/admin_deposit_account_creator.cdc) + +## 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. +This can be stored in their private storage or a Capability Receiver. + +**Account 1:** `/private/capability` → `/storage/resource` + +`/private/revokableLink` -> `/private/capability` + +**Account 2:** `Capability Receiver(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 + ) + } +} +``` \ No newline at end of file diff --git a/docs/measuring-time.mdx b/docs/measuring-time.mdx index 0aaad8a5bd..9ea727775a 100644 --- a/docs/measuring-time.mdx +++ b/docs/measuring-time.mdx @@ -2,32 +2,57 @@ title: Measuring Time In Cadence --- -## Time On The Flow Blockchain +## Accessing Time From Cadence -> Flow targets 1 second block times but the protocol is still early in its development and further optimizations are needed to achieve that. As of Feb 2021, the rate of block finalization on Mainnet is 0.4 blocks/s; with a standard deviation of ±0.1 blocks/s. Hence, a new block is finalized on average every 2.5 seconds. Note that block height only has a loose correlation with time, as the block rate naturally fluctuates. +Both the block height and the block timestamp are accessible from within Cadence code: -[https://docs.onflow.org/faq/operators#does-the-blockheight-go-up-1-every-second](https://docs.onflow.org/faq/operators#does-the-blockheight-go-up-1-every-second) +[https://docs.onflow.org/cadence/language/environment-information/#block-information](https://docs.onflow.org/cadence/language/environment-information/#block-information) -In addition to the natural variation described above, there are several theoretical block production attacks that could skew this relationship even further. These attacks are unlikely on Flow in the absence of byzantine nodes. The timestamp cannot be earlier than the timestamp of the previous block, and cannot be too far into the future ([currently ten seconds](https://github.com/onflow/flow-go/blob/master/module/builder/consensus/builder.go#L60)) - proposed blocks that fail to satisfy these conditions will be rejected by Flow's consensus algorithm. But the mere possibility of these attacks places an additional limit on the confidence with which we can use block heights or block timestamps to determine off-chain time from protocol-level data on-chain. +This means that they can be used to calculate dates and durations by smart contracts on Flow +that need to lock resources until a particular point in the future, calculate values between a range of dates, +or otherwise deal with the passage of time. -The block timestamp is not the only way to identify a block within the flow of off-chain time. Each block is numbered successively by its "height", block 70000 is followed by block 70001, 70002, and so on. Blocks with heights out of sequence are rejected by Flow's consensus algorithm. In theory the timestamp on a block should be roughly equivalent to the timestamp on the Flow genesis block, plus the block height multiplied by the target block rate. But as we have seen both the target and the on-chain average rate of block production may vary over time. This makes such calculations more difficult. +There are two popular strategies that are used to measure time on blockchains: -## Accessing Time From Cadence +1. Use the timestamp, and optionally check that the average duration of the last n blocks +is close enough to the block target duration to make an attack unlikely. +2. Use the block height directly. Block height can be treated intuitively +(a hundred blocks, a thousand blocks) or can be related to estimated timestamps +and thereby to time off-chain by the methods described in this article. -Both the block height and the block timestamp are accessible from within Cadence code: +## Time On The Flow Blockchain -[https://docs.onflow.org/cadence/language/environment-information/#block-information](https://docs.onflow.org/cadence/language/environment-information/#block-information) +> Flow targets 1 second block times but the protocol is still early in its development +and further optimizations are needed to achieve that. +As of Feb 2021, the rate of block finalization on Mainnet is more than 0.5 blocks/s; with a standard deviation of ±0.1 blocks/s. +Hence, a new block is finalized on average every 2 seconds. +Note that block height only has a loose correlation with time, as the block rate naturally fluctuates. -This means that they can be used to calculate dates and durations by smart contracts on Flow that need to lock resources until a particular point in the future, calculate values between a range of dates, or otherwise deal with the passage of time. +[https://docs.onflow.org/faq/operators#does-the-blockheight-go-up-1-every-second](https://docs.onflow.org/faq/operators#does-the-blockheight-go-up-1-every-second) -There are two popular strategies that are used to measure time on blockchains: +In addition to the natural variation described above, +there are several theoretical block production attacks that could skew this relationship even further. +These attacks are unlikely on Flow in the absence of byzantine nodes. +The timestamp cannot be earlier than the timestamp of the previous block, +and cannot be too far into the future ([currently ten seconds](https://github.com/onflow/flow-go/blob/master/module/builder/consensus/builder.go#L60)) - proposed blocks that fail to satisfy these conditions will be rejected by Flow's consensus algorithm. But the mere possibility of these attacks places an additional limit on the confidence with which we can use block heights or block timestamps to determine off-chain time from protocol-level data on-chain. -1. Use the timestamp, and optionally check that the average duration of the last n blocks is close enough to the block target duration to make an attack unlikely. -2. Use the block height directly. Block height can be treated intuitively (a hundred blocks, a thousand blocks) or can be related to estimated timestamps and thereby to time off-chain by the methods described in this article. +The block timestamp is not the only way to identify a block within the flow of off-chain time. +Each block is numbered successively by its "height", block 70000 is followed by block 70001, 70002, +and so on. Blocks with heights out of sequence are rejected by Flow's consensus algorithm. +In theory the timestamp on a block should be roughly equivalent to the timestamp on the Flow genesis block, +plus the block height multiplied by the target block rate. +But as we have seen both the target and the on-chain average rate of block production may vary over time. +This makes such calculations more difficult. ### Using The Timestamp -Given that [Flow consensus will reject new blocks with a timestamp more than ten seconds into the future from the previous block](https://github.com/onflow/flow-go/blob/1e8a2256171d5fd576f442d0c335c9bcc06e1e09/module/builder/consensus/builder.go#L525-L536), as long as you do not require an accuracy of less than ten seconds it is probably safe to use the block timestamp for events lasting a few days - in the absence of a change in block production rate targets. Or, more intuitively, your timestamp is highly likely to be the correct hour, very likely to be the correct minute, and may well be within ten seconds of the correct second. Which of these scales is tolerable for your use case depends on how long the events you need to represent will take. In an auction lasting several days, you are probably safe with any scale above ten seconds. +Given that [Flow consensus will reject new blocks with a timestamp more than ten seconds into the future from the previous block](https://github.com/onflow/flow-go/blob/1e8a2256171d5fd576f442d0c335c9bcc06e1e09/module/builder/consensus/builder.go#L525-L536), +as long as you do not require an accuracy of less than ten seconds +it is probably safe to use the block timestamp for events lasting a few days - in the absence of a change in block production rate targets. +Or, more intuitively, your timestamp is highly likely to be the correct hour, +very likely to be the correct minute, and may well be within ten seconds of the correct second. +Which of these scales is tolerable for your use case depends on how long the events you need to represent will take. +In an auction lasting several days, you are probably safe with any scale above ten seconds. ```cadence // To get the timestamp of the block that the code is being executed in @@ -39,7 +64,13 @@ getBlock(at: 70001)?.timestamp ### Using The Block Height -In theory block numbers are more reliable than timestamps, as the block height is incremented for each block in a fork. But in practice we must still relate block numbers to off-chain time values, and to do this requires that we assume that the average block time will hold. This can vary due to factors other than attacks. Given that block time targets will vary as Flow development continues, this will affect any calculations you may make in order to relate block numbers to calendar time. +In theory block numbers are more reliable than timestamps, +as the block height is incremented for each block in a fork. +But in practice we must still relate block numbers to off-chain time values, +and to do this requires that we assume that the average block time will hold. +This can vary due to factors other than attacks. +Given that block time targets will vary as Flow development continues, +this will affect any calculations you may make in order to relate block numbers to calendar time. ```cadence // To get the block number of the block that the code is being executed in @@ -51,8 +82,15 @@ getBlock(at: 70001)?.height ## Recommendations -If your contract code can tolerate the limitations described above, use block timestamps. If not, you may need to consider more exotic solutions (time oracles, etc.). +If your contract code can tolerate the limitations described above, use block timestamps. +If not, you may need to consider more exotic solutions (time oracles, etc.). -Whichever method you use, be careful not to hardcode any assumptions about block rates production rates into your code, on-chain or off, in a way that cannot be updated later. +Whichever method you use, be careful not to hardcode any assumptions +about block rates production rates into your code, on-chain or off, +in a way that cannot be updated later. -On-chain auctions and similar mechanisms should always have an extension mechanism. If someone bids at the last moment (which is easier to do with a block production attack), the end time for the auction extends (if necessary) to N minutes past the last bid. (10 minutes, 30 minutes, an hour). As N increases, this becomes more secure: N=5 should be more than enough. with the current parameters of the Flow blockchain. +On-chain auctions and similar mechanisms should always have an extension mechanism. +If someone bids at the last moment (which is easier to do with a block production attack), +the end time for the auction extends (if necessary) to N minutes past the last bid. +(10 minutes, 30 minutes, an hour). As N increases, this becomes more secure: +N=5 should be more than enough. with the current parameters of the Flow blockchain. diff --git a/docs/msg-sender.mdx b/docs/msg-sender.mdx index 8127579382..49dbad50bd 100644 --- a/docs/msg-sender.mdx +++ b/docs/msg-sender.mdx @@ -2,19 +2,44 @@ title: msg․sender Considered Harmful --- -One question that every Solidity user asks when they start programming in Cadence is "how do I check `msg.sender`?". On Ethereum, checking `msg.sender` is used to identify the account that is calling a function and to modify that function's behaviour appropriately. Doing so is key to identity, permissions, ownership and security on Ethereum. +One question that every Solidity user asks when they start programming in Cadence is -Cadence does not have `msg.sender` and there is no transaction-level way for Cadence code to uniquely identify its caller, not least because each transaction can be signed by more than one account. This then translates to the transaction having access to all of the signers' accounts. A further difference from Ethereum is that while Ethereum and Flow both have accounts that can contain contract code and data, in Cadence the resources that individual users own (such as NFTs) are placed in the *user* account's storage area rather than the contract account's. +**"How do I get the account who authorized the transaction?"** -All of this means that a task that would be implemented by a single central contract checking `msg.sender` in Solidity will require a different approach in Cadence. The design of Cadence is intentionally different, it follows a [Capability-based security model](https://en.wikipedia.org/wiki/Capability-based_security) instead of an [Access-Control List-based model](https://en.wikipedia.org/wiki/Access-control_list). We believe that this design offers distinct advantages for code robustness and security. This article describes how to perform some common tasks in idiomatic ways that exploit those advantages and also describes some ways of approaching the same tasks that should be avoided. +In the Ethereum world, this account is referred to as `msg.sender`. +On Ethereum, checking `msg.sender` is used to modify a function's behaviour depending on who authorized it. +Doing so is key to identity, permissions, ownership and security on Ethereum. + +Cadence does not have `msg.sender` and there is no transaction-level way +for Cadence code to uniquely identify its caller, +not least because each transaction can be signed by more than one account. + +This then translates to the transaction having access to all of the signers' accounts. +A further difference from Ethereum is that while Ethereum and Flow both have accounts +that can contain contract code and data, in Cadence the resources that individual users own (such as NFTs) +are placed in the *user* account's storage area rather than the contract account's. + +All of this means that a task that would be implemented +by a single central contract checking `msg.sender` in Solidity +will require a different approach in Cadence. The design of Cadence is intentionally different; +it follows a [Capability-based security model](https://en.wikipedia.org/wiki/Capability-based_security) +instead of an [Access-Control List-based model](https://en.wikipedia.org/wiki/Access-control_list). + +This design offers distinct advantages for code robustness and security. + +This article describes how to perform some common tasks in idiomatic ways +that use those advantages and also describes some ways of approaching +the same tasks that should be avoided. # Patterns ## Admin Rights -Admin facilities should be contained in Admin resources. This can be a single resource with capabilities to it provided through different interfaces to expose different functionality for different roles, or it can be different resources for each role. +Admin facilities should be contained in Admin resources. +This can be a single resource with capabilities to it provided through different interfaces +to expose different functionality for different roles, or it can be different resources for each role. -This is described in the Patterns document: +This is described in the Design Patterns document: [https://docs.onflow.org/cadence/design-patterns#init-singleton](https://docs.onflow.org/cadence/design-patterns#init-singleton) @@ -22,7 +47,8 @@ A good example of this is minting tokens: [https://github.com/onflow/kitty-items/KittyItems.cdc](https://github.com/onflow/kitty-items/blob/438988b6e7e100f46687cbe379bd7b0b88fd8ef4/cadence/contracts/KittyItems.cdc#L194) -Where access to admin functionality must be given to several different accounts and/or be revocable, the Capability Receiver pattern supports this. +Where access to admin functionality must be given to several different accounts and/or be revocable, +the Capability Receiver pattern supports this. This is described in the Patterns document: @@ -32,15 +58,19 @@ This is described in the Patterns document: ## Allow/Block Listing -Limiting a user's control of resources that they own except in exceptional circumstances is considered un-Flow-like. If you must implement allow/block listing of accounts for regulatory compliance, route calls from functions in your resources through `access(contract)` code on their contract that checks an admin-controlled dictionary containing the information required to check for allowed or blocked accounts. +Limiting a user's control of resources that they own except in exceptional circumstances +is considered un-Flow-like. If you must implement allow/block listing of accounts +for regulatory compliance, route calls from functions in your resources +through `access(contract)` code on their contract that checksan admin-controlled dictionary +containing the information required to check for allowed or blocked accounts. -This code could check the resource owner, but doing so is an antipattern (see below) and should not be used as it cannot be relied on. It is better to use an object's `uuid`: +This code could check the resource owner, but doing so is an antipattern (see below) +and should not be used as it cannot be relied on. [It is better to use an object's `uuid`:](https://docs.onflow.org/cadence/language/resources/#resource-identifier) -[https://docs.onflow.org/cadence/language/resources/#resource-identifier](https://docs.onflow.org/cadence/language/resources/#resource-identifier) - -For example: [forthcoming] - -It is important to note that the uuid does not identify the owner, and that resources can be transferred to different owners, and moved to a different path within the same user's storage or replaced by a different resource at the same path. +It is important to note that the uuid does not identify the owner, +and that resources can be transferred to different owners, +and moved to a different path within the same user's storage +or replaced by a different resource at the same path. ## Operator/Allowance @@ -48,72 +78,66 @@ Giving another user temporary partial control of resources should be implemented ### Direct Capabilities -Limiting access to the correct resources can be achieved by (e.g.) creating a new Vault containing only the allowance amount, or creating a new Collection containing only the NFTs that the other user is the operator for. +Limiting access to the correct resources can be achieved by (e.g.) +creating a new Vault containing only the allowance amount, +or creating a new Collection containing only the NFTs that the other user is the operator for. -Limiting access to the correct functionality can be achieved by providing the other user with a capability constrained to the desired interface. +Limiting access to the correct functionality can be achieved +by providing the other user with a capability constrained to the desired interface. The capability can be revoked to remove the ability when required. -Revokable Capabilities are described in the Patterns document: - -[https://docs.onflow.org/cadence/design-patterns#capability-revocation](https://docs.onflow.org/cadence/design-patterns#capability-revocation) - -And they are discussed in the Tutorial: +[Revokable Capabilities are described in the Patterns document](/cadence/design-patterns#capability-revocation)]: -[https://docs.onflow.org/cadence/tutorial/02-hello-world#creating-capabilities-and-references-to-stored-resources](https://docs.onflow.org/cadence/tutorial/02-hello-world#creating-capabilities-and-references-to-stored-resources) +[And they are discussed in the Tutorial](/cadence/tutorial/02-hello-world#creating-capabilities-and-references-to-stored-resources): ### Wrapped Capabilities -Alternatively, a capability on the original resource (Vault, Collection, etc.) can be wrapped in a resource that enforces all of these limits, and then this (or, preferably, a capability to it) passed to the other user. +Alternatively, a capability on the original resource (Vault, Collection, etc.) +can be wrapped in a resource that enforces all of these limits, +and then this (or, preferably, a capability to it) passed to the other user. -For example, see KittyItemsMarket's carefully constrained use of a NonFungibleToken.Provider: - -[https://github.com/onflow/kitty-items/cadence/contracts/KittyItemsMarket.cdc](https://github.com/onflow/kitty-items/blob/438988b6e7e100f46687cbe379bd7b0b88fd8ef4/cadence/contracts/KittyItemsMarket.cdc#L84) +For example, [see KittyItemsMarket's carefully constrained use](https://github.com/onflow/kitty-items/blob/438988b6e7e100f46687cbe379bd7b0b88fd8ef4/cadence/contracts/KittyItemsMarket.cdc#L84) +of a NonFungibleToken.Provider: ## Ownership -If an account's storage contains a resource (such as an NFT, and NFT Collection, or an FT Vault), that account owns it. There is no need to record this anywhere else. It can be checked through public capabilities. If the user removes the public capabilities, that is their choice. +If an account's storage contains a resource (such as an NFT, and NFT Collection, or an FT Vault), +that account owns it. There is no need to record this anywhere else. +It can be checked through public capabilities. +If the user removes the public capabilities, that is their choice. -For example the Collection resource in the NFT standard, its interfaces, and the Capabilities to it placed in a user's storage: +For example the Collection resource in the NFT standard, its interfaces, +and the Capabilities to it placed in a user's storage: [https://github.com/onflow/flow-nft/contracts/ExampleNFT.cdc](https://github.com/onflow/flow-nft/blob/cda8ccde7db52a0e125650b87bb552788ad15373/contracts/ExampleNFT.cdc#L26) -Custodial NFT marketplaces have temporary ownership of a resource. They should provide the ability to identify the token's original owner and to return it to them if it is not sold. - -For example: [forthcoming] +Custodial NFT marketplaces have temporary ownership of a resource. +They should provide the ability to identify the token's original owner +and to return it to them if it is not sold. ## User Profiles -User profiles can be implemented as resources placed in the storage of the user's account, with read access via a public capability. - -Admin control of user profiles, where appropriate, can be implemented using private capabilities, `access(contract)` code, or using types within the contract that can only be `create`d by the admin as function arguments. +User profiles can be implemented as resources placed in the storage of the user's account, +with read access via a public capability. -For example: [forthcoming] +Admin control of user profiles, where appropriate, can be implemented using private capabilities, +`access(contract)` code, or using types within the contract that can only be created +by the admin as function arguments. # Antipatterns ## Checking Contract.account -Contracts have the member variable `let account: Account`, which is the account in which the contract is deployed: +Contracts [have the member variable `let account: Account`]((/cadence/language/contracts/)), +which is the account in which the contract is deployed: -[https://docs.onflow.org/cadence/language/contracts/](https://docs.onflow.org/cadence/language/contracts/) - -This is of limited use in replacing `msg.sender`, as it is essentially a tautology on Flow because contracts are deployed to the account to which you deploy them. +This is of limited use in replacing `msg.sender`, as it is essentially a tautology on Flow +because contracts are deployed to the account to which you deploy them. ## Checking Resource.owner -Resources that are in storage (but not those that are located in-memory, e.g. when a resource has just been created) have the implicit field `let owner: PublicAccount?` : - -[https://docs.onflow.org/cadence/language/resources/#resource-owner](https://docs.onflow.org/cadence/language/resources/#resource-owner) +Resources that are in storage (but not those that are located in-memory, +e.g. when a resource has just been created) [have the implicit field `let owner: PublicAccount?`]((/cadence/language/resources/#resource-owner)) This can be defeated by using a newly created resource, as the owner will then be nil. - -## Passing An AuthAccount Into Contract Code - -You should ***never*** require that the user passes an AuthAccount into your contract code for any reason. This includes for authentication purposes. - -AuthAccounts provide unlimited access to a user's account, including full access to the account's public keys and all of its storage. This can be constrained in transactions but it is harder to reason about when passed into contract code. - -The only exception to this is where you are deploying your own contract to an account that you control and wish to deposit a resource into a separate account. In this instance, you can pass an AuthAccount into the initializer. But this introduces a co-ordination problem in that you must get signatures from both accounts. Either depositing the resource into the contract's storage and using a separate transaction to move it to the other account, or the capability receiver pattern, are just as effective: - -[https://docs.onflow.org/cadence/design-patterns#capability-receiver](https://docs.onflow.org/cadence/design-patterns#capability-receiver)