From 903c0d8437e9092562df0f8585d221091464366c Mon Sep 17 00:00:00 2001 From: Jan Bernatik Date: Wed, 8 Nov 2023 16:25:32 +0100 Subject: [PATCH 1/2] Moved 0.42 to /docs as current so that /docs/... routing works --- .../0.42 => docs}/_category_.yml | 0 docs/anti-patterns.md | 210 ++++-- docs/design-patterns.md | 214 +++--- docs/index.md | 2 +- docs/json-cadence-spec.md | 9 +- docs/language/access-control.md | 567 +++------------- .../0.42 => docs}/language/accounts.mdx | 0 docs/language/accounts/index.mdx | 4 +- docs/language/accounts/paths.mdx | 2 +- docs/language/attachments.mdx | 137 +--- docs/language/built-in-functions.mdx | 103 ++- docs/language/capabilities.md | 293 +++++++-- docs/language/composite-types.mdx | 77 +-- docs/language/contract-updatability.md | 136 ++-- docs/language/contracts.mdx | 358 ++++++++-- docs/language/core-events.md | 23 +- docs/language/crypto.mdx | 84 ++- docs/language/enumerations.md | 10 +- docs/language/environment-information.md | 10 +- docs/language/events.md | 6 +- docs/language/functions.mdx | 129 +--- docs/language/glossary.mdx | 18 +- docs/language/imports.mdx | 4 +- docs/language/interfaces.mdx | 527 ++------------- docs/language/operators.md | 1 - .../0.42 => docs}/language/references.md | 0 docs/language/resources.mdx | 32 +- .../language/restricted-types.md | 0 docs/language/run-time-types.md | 29 +- docs/language/transactions.md | 226 +++---- docs/language/type-inference.md | 2 +- docs/language/values-and-types.mdx | 191 +----- docs/measuring-time.mdx | 2 +- docs/solidity-to-cadence.md | 66 +- docs/testing-framework.mdx | 12 +- docs/tutorial/01-first-steps.md | 6 +- docs/tutorial/02-hello-world.md | 24 +- docs/tutorial/03-resources.md | 90 +-- docs/tutorial/04-capabilities.md | 73 ++- docs/tutorial/05-non-fungible-tokens-1.md | 53 +- docs/tutorial/05-non-fungible-tokens-2.md | 113 ++-- docs/tutorial/06-fungible-tokens.md | 32 +- docs/tutorial/07-marketplace-setup.md | 10 +- docs/tutorial/08-marketplace-compose.md | 88 +-- .../0.42 => docs}/tutorial/09-voting.mdx | 0 docs/tutorial/10-resources-compose.md | 36 +- docusaurus.config.js | 10 +- versioned_docs/version-1.0/anti-patterns.md | 250 +++++++ .../0.42 => version-1.0}/contract-upgrades.md | 0 .../0.42 => version-1.0}/design-patterns.md | 214 +++--- .../0.42 => version-1.0}/index.md | 2 +- .../0.42 => version-1.0}/json-cadence-spec.md | 9 +- .../language/_category_.json | 0 .../version-1.0/language/access-control.md | 610 ++++++++++++++++++ .../language/accounts/_category_.json | 0 .../language/accounts/capabilities.mdx | 0 .../language/accounts/contracts.mdx | 0 .../language/accounts/inbox.mdx | 0 .../language/accounts/index.mdx | 4 +- .../language/accounts/keys.mdx | 0 .../language/accounts/paths.mdx | 2 +- .../language/accounts/storage.mdx | 0 .../language/attachments.mdx | 137 +++- .../language/built-in-functions.mdx | 95 +++ .../version-1.0/language/capabilities.md | 70 ++ .../language/composite-types.mdx | 77 ++- .../language/constants-and-variables.md | 0 .../language/contract-updatability.md | 136 ++-- .../version-1.0/language/contracts.mdx | 258 ++++++++ .../language/control-flow.md | 0 .../language/core-events.md | 23 +- .../0.42 => version-1.0}/language/crypto.mdx | 84 +-- .../language/enumerations.md | 10 +- .../language/environment-information.md | 10 +- .../0.42 => version-1.0}/language/events.md | 6 +- .../language/functions.mdx | 129 +++- .../language/glossary.mdx | 18 +- .../0.42 => version-1.0}/language/imports.mdx | 4 +- .../0.42 => version-1.0}/language/index.md | 0 .../language/interfaces.mdx | 527 +++++++++++++-- .../language/intersection-types.md | 0 .../language/operators.md | 1 + .../version-1.0}/language/references.mdx | 0 .../language/resources.mdx | 32 +- .../language/run-time-types.md | 29 +- .../0.42 => version-1.0}/language/scope.md | 0 .../0.42 => version-1.0}/language/syntax.md | 0 .../version-1.0/language/transactions.md | 246 +++++++ .../language/type-annotations.md | 0 .../language/type-hierarchy.md | 0 .../language/type-hierarchy.monopic | Bin .../language/type-hierarchy.png | Bin .../language/type-inference.md | 2 +- .../language/type-safety.md | 0 .../language/values-and-types.mdx | 191 +++++- .../0.42 => version-1.0}/measuring-time.mdx | 2 +- .../project-development-tips.md | 0 .../security-best-practices.md | 0 .../solidity-to-cadence.md | 66 +- .../testing-framework.mdx | 12 +- .../tutorial/01-first-steps.md | 6 +- .../tutorial/02-hello-world.md | 24 +- .../tutorial/03-resources.md | 90 ++- .../tutorial/04-capabilities.md | 73 +-- .../tutorial/05-non-fungible-tokens-1.md | 53 +- .../tutorial/05-non-fungible-tokens-2.md | 113 ++-- .../tutorial/06-fungible-tokens.md | 32 +- .../tutorial/07-marketplace-setup.md | 10 +- .../tutorial/08-marketplace-compose.md | 88 +-- .../version-1.0}/tutorial/09-voting.md | 0 .../tutorial/10-resources-compose.md | 36 +- .../tutorial/_category_.json | 0 .../tutorial/deploy_approval_voting.png | Bin .../tutorial/deploy_basic_token.png | Bin .../tutorial/deploy_example_token.png | Bin .../tutorial/deploy_kittyverse.png | Bin .../tutorial/deploybox.png | Bin .../tutorial/playground-intro.png | Bin .../0.42 => version-1.0}/why.md | 0 .../0.42/anti-patterns.md | 382 ----------- .../0.42/language/access-control.md | 223 ------- .../0.42/language/built-in-functions.mdx | 90 --- .../0.42/language/capabilities.md | 257 -------- .../0.42/language/contracts.mdx | 496 -------------- .../0.42/language/transactions.md | 226 ------- versioned_sidebars/version-1.0-sidebars.json | 4 + .../version-current_0.42-sidebars.json | 4 - versions.json | 2 +- 128 files changed, 4742 insertions(+), 4742 deletions(-) rename {versioned_docs/version-current_0.42/0.42 => docs}/_category_.yml (100%) rename {versioned_docs/version-current_0.42/0.42 => docs}/language/accounts.mdx (100%) rename {versioned_docs/version-current_0.42/0.42 => docs}/language/references.md (100%) rename {versioned_docs/version-current_0.42/0.42 => docs}/language/restricted-types.md (100%) rename {versioned_docs/version-current_0.42/0.42 => docs}/tutorial/09-voting.mdx (100%) create mode 100644 versioned_docs/version-1.0/anti-patterns.md rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/contract-upgrades.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/design-patterns.md (62%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/index.md (99%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/json-cadence-spec.md (99%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/_category_.json (100%) create mode 100644 versioned_docs/version-1.0/language/access-control.md rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/_category_.json (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/capabilities.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/contracts.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/inbox.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/index.mdx (98%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/keys.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/paths.mdx (90%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/accounts/storage.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/attachments.mdx (61%) create mode 100644 versioned_docs/version-1.0/language/built-in-functions.mdx create mode 100644 versioned_docs/version-1.0/language/capabilities.md rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/composite-types.mdx (89%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/constants-and-variables.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/contract-updatability.md (83%) create mode 100644 versioned_docs/version-1.0/language/contracts.mdx rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/control-flow.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/core-events.md (89%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/crypto.mdx (91%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/enumerations.md (94%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/environment-information.md (91%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/events.md (96%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/functions.mdx (78%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/glossary.mdx (92%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/imports.mdx (91%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/index.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/interfaces.mdx (52%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/intersection-types.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/operators.md (99%) rename {docs => versioned_docs/version-1.0}/language/references.mdx (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/resources.mdx (95%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/run-time-types.md (90%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/scope.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/syntax.md (100%) create mode 100644 versioned_docs/version-1.0/language/transactions.md rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-annotations.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-hierarchy.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-hierarchy.monopic (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-hierarchy.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-inference.md (98%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/type-safety.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/language/values-and-types.mdx (89%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/measuring-time.mdx (98%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/project-development-tips.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/security-best-practices.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/solidity-to-cadence.md (93%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/testing-framework.mdx (98%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/01-first-steps.md (97%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/02-hello-world.md (94%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/03-resources.md (89%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/04-capabilities.md (88%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/05-non-fungible-tokens-1.md (89%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/05-non-fungible-tokens-2.md (87%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/06-fungible-tokens.md (97%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/07-marketplace-setup.md (97%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/08-marketplace-compose.md (92%) rename {docs => versioned_docs/version-1.0}/tutorial/09-voting.md (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/10-resources-compose.md (91%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/_category_.json (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/deploy_approval_voting.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/deploy_basic_token.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/deploy_example_token.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/deploy_kittyverse.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/deploybox.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/tutorial/playground-intro.png (100%) rename versioned_docs/{version-current_0.42/0.42 => version-1.0}/why.md (100%) delete mode 100644 versioned_docs/version-current_0.42/0.42/anti-patterns.md delete mode 100644 versioned_docs/version-current_0.42/0.42/language/access-control.md delete mode 100644 versioned_docs/version-current_0.42/0.42/language/built-in-functions.mdx delete mode 100644 versioned_docs/version-current_0.42/0.42/language/capabilities.md delete mode 100644 versioned_docs/version-current_0.42/0.42/language/contracts.mdx delete mode 100644 versioned_docs/version-current_0.42/0.42/language/transactions.md create mode 100644 versioned_sidebars/version-1.0-sidebars.json delete mode 100644 versioned_sidebars/version-current_0.42-sidebars.json diff --git a/versioned_docs/version-current_0.42/0.42/_category_.yml b/docs/_category_.yml similarity index 100% rename from versioned_docs/version-current_0.42/0.42/_category_.yml rename to docs/_category_.yml diff --git a/docs/anti-patterns.md b/docs/anti-patterns.md index 6acc46f..726ccf3 100644 --- a/docs/anti-patterns.md +++ b/docs/anti-patterns.md @@ -6,16 +6,17 @@ sidebar_label: Anti-Patterns This is an opinionated list of issues that can be improved if they are found in Cadence code intended for production. -## Avoid using authorized account references as a function parameter +# Security and Robustness -### Problem +## Avoid using `AuthAccount` as a function parameter -A developer may choose to authenticate or perform operations for their users by using the users' account addresses. -In order to do this, they might add a parameter to a function which has an authorized account reference type (`auth(...) &Account`), -as an authorized account reference can only be obtained by signing a transaction. +### Problem -This is problematic, as the authorized account reference allows access to some sensitive operations on the account, -for example, to write to storage, +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: @@ -25,12 +26,12 @@ which provides the opportunity for bad actors to take advantage of. // BAD CODE // DO NOT COPY -// Imagine this code is in a contract that uses a `auth(Storage) &Account` parameter -// to authenticate users to transfer NFTs +// 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 -access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { +pub fun transferNFT(id: UInt64, owner: AuthAccount) { assert(owner(id) == owner.address) transfer(id) @@ -42,7 +43,7 @@ access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { // should not be accessible in this function // BAD -access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { +pub fun transferNFT(id: UInt64, owner: AuthAccount) { assert(owner(id) == owner.address) transfer(id) @@ -66,9 +67,92 @@ Projects should find other ways to authenticate users, such as using resources a 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 authorized account reference (`auth(...) &Account`) is necessary, -such as a cold storage multi-sig, -but those cases are rare and such usage should still be avoided unless absolutely necessary. +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](./language/references.md) 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 @@ -80,9 +164,7 @@ Accidentally exposed fields can be a security hole. ### Solution When writing your smart contract, look at every field and function and make sure -that require access through an [entitlement](./language/access-control.md#entitlements) (`access(E)`), -or use a non-public [access modifier](./language/access-control.md) like `access(self)`, `access(contract)`, or `access(account)`, -unless otherwise needed. +that they are all `access(self)`, `access(contract)`, or `access(account)`, unless otherwise needed. ## Capability-Typed public fields are a security hole @@ -100,6 +182,55 @@ has been stored as a field on a contract or resource in this way. 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. @@ -111,8 +242,9 @@ If that resource provides access to admin functions then the creation function s ### Solution -To fix this, a single instance of that resource should be created in the contract's initializer, +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 @@ -120,14 +252,14 @@ and then a new creation function can be potentially included within the admin re // Pseudo-code // BAD -access(all) contract Currency { - access(all) resource Admin { - access(all) fun mintTokens() +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 - access(all) fun createAdmin(): @Admin { + pub fun createAdmin(): @Admin { return <-create Admin() } } @@ -135,12 +267,12 @@ access(all) contract Currency { // This contract makes the admin creation private and in the initializer // so that only the one who controls the account can mint tokens // GOOD -access(all) contract Currency { - access(all) resource Admin { - access(all) fun mintTokens() +pub contract Currency { + pub resource Admin { + pub fun mintTokens() // Only an admin can create new Admins - access(all) fun createAdmin(): @Admin { + pub fun createAdmin(): @Admin { return <-create Admin() } } @@ -149,8 +281,8 @@ access(all) contract Currency { // Create a single admin resource let firstAdmin <- create Admin() - // Store it in private account storage, so only the admin can use it - self.account.storage.save(<-firstAdmin, to: /storage/currencyAdmin) + // Store it in private account storage in `init` so only the admin can use it + self.account.save(<-firstAdmin, to: /storage/currencyAdmin) } } ``` @@ -168,7 +300,7 @@ which means that anyone can create a new instance of a struct without going thro ### Solution Any contract state-modifying operations related to the creation of structs -should be contained in resources instead of the initializers of structs. +should be contained in restricted resources instead of the initializers of structs. ### Example @@ -181,14 +313,14 @@ which increments the number that tracks the play IDs and emits an event: // Simplified Code // BAD // -access(all) contract TopShot { +pub contract TopShot { // The Record that is used to track every unique play ID - access(all) var nextPlayID: UInt32 + pub var nextPlayID: UInt32 - access(all) struct Play { + pub struct Play { - access(all) let playID: UInt32 + pub let playID: UInt32 init() { @@ -216,24 +348,24 @@ that creates the plays. // Update contract state in admin resource functions // GOOD // -access(all) contract TopShot { +pub contract TopShot { // The Record that is used to track every unique play ID - access(all) var nextPlayID: UInt32 + pub var nextPlayID: UInt32 - access(all) struct Play { + pub struct Play { - access(all) let playID: UInt32 + pub let playID: UInt32 init() { self.playID = TopShot.nextPlayID } } - access(all) resource Admin { + pub resource Admin { // Protected within the private admin resource - access(all) fun createPlay() { + pub fun createPlay() { // Create the new Play var newPlay = Play() diff --git a/docs/design-patterns.md b/docs/design-patterns.md index 186b5d2..24f6796 100644 --- a/docs/design-patterns.md +++ b/docs/design-patterns.md @@ -29,7 +29,7 @@ See [Wikipedia's page on magic numbers](https://en.wikipedia.org/wiki/Magic_numb ### Solution -Add a public (`access(all)`), constant (`let`) field, e.g. a `Path` , to the contract responsible for the value, +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. @@ -38,27 +38,27 @@ Example Snippet: ```cadence // BAD Practice: Do not hard code storage paths -access(all) contract NamedFields { - access(all) resource Test {} +pub contract NamedFields { + pub resource Test {} init() { // BAD: Hard-coded storage path - self.account.storage.save(<-create Test(), to: /storage/testStorage) + self.account.save(<-create Test(), to: /storage/testStorage) } } // GOOD practice: Instead, use a field // -access(all) contract NamedFields { - access(all) resource Test {} +pub contract NamedFields { + pub resource Test {} // GOOD: field storage path - access(all) let TestStoragePath: StoragePath + pub let TestStoragePath: StoragePath init() { // assign and access the field here and in transactions self.TestStoragePath = /storage/testStorage - self.account.storage.save(<-create Test(), to: self.TestStoragePath) + self.account.save(<-create Test(), to: self.TestStoragePath) } } @@ -74,13 +74,14 @@ 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. +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. +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 `access(all)` and exposing it via a `/public/` capability will allow this. +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. @@ -91,7 +92,7 @@ Example: access(self) let totalSupply: UFix64 // GOOD: Field is public, so it can be read and used by anyone -access(all) let totalSupply: UFix64 +pub let totalSupply: UFix64 ``` ## Script-Accessible report @@ -114,12 +115,12 @@ 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 +### Example Code ```cadence -access(all) contract AContract { - access(all) let BResourceStoragePath: StoragePath - access(all) let BResourcePublicPath: PublicPath +pub contract AContract { + pub let BResourceStoragePath: StoragePath + pub let BResourcePublicPath: PublicPath init() { self.BResourceStoragePath = /storage/BResource @@ -127,15 +128,15 @@ access(all) contract AContract { } // Resource definition - access(all) resource BResource { - access(all) var c: UInt64 - access(all) var d: String + 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 - access(all) fun generateReport(): BReportStruct { + pub fun generateReport(): BReportStruct { return BReportStruct(c: self.c, d: self.d) } @@ -146,9 +147,9 @@ access(all) contract AContract { } // Define a struct with the same fields as the resource - access(all) struct BReportStruct { - access(all) var c: UInt64 - access(all) var d: String + pub struct BReportStruct { + pub var c: UInt64 + pub var d: String init(c: UInt64, d: String) { self.c = c @@ -162,26 +163,26 @@ access(all) contract AContract { import AContract from 0xAContract transaction { - prepare(acct: auth(IssueStorageCapabilityController, PublishCapability) &Account) { + prepare(acct: AuthAccount) { //... - let cap = acct.capabilities.storage.issue<&AContract.BResource>(AContract.BResourceStoragePath) - acct.capabilities.publish(cap, at: AContract.BResourcePublicPath) + acct.link<&AContract.BResource>(AContract.BResourcePublicPath, target: AContract.BResourceStoragePath) } } // Script import AContract from 0xAContract // Return the struct with a script -access(all) fun main(account: Address): AContract.BReportStruct { +pub fun main(account: Address): AContract.BReportStruct { // borrow the resource - let b = getAccount(account).capabilities - .borrow<&AContract.BResource>(AContract.BResourcePublicPath) + let b = getAccount(account) + .getCapability(AContract.BResourcePublicPath) + .borrow<&AContract.BResource>() // return the struct return b.generateReport() } ``` -## Init singleton +## Init Singleton ### Problem @@ -190,10 +191,10 @@ There should not be a function to do this, as that would allow anyone to create ### Solution -Create any one-off resources in the contract's initializer -and deliver them to an address or `&Account` 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 initializer: +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) @@ -221,16 +222,15 @@ All fields, functions, types, variables, etc., need to have names that clearly d `/storage/bestPracticesDocsCollectionPath` is better than `/storage/collection` ### Example - ```cadence // BAD: Unclear naming // -access(all) contract Tax { +pub contract Tax { // Do not use abbreviations unless absolutely necessary - access(all) var pcnt: UFix64 + pub var pcnt: UFix64 // Not clear what the function is calculating or what the parameter should be - access(all) fun calculate(num: UFix64): UFix64 { + pub fun calculate(num: UFix64): UFix64 { // What total is this referring to? let total = num + (num * self.pcnt) @@ -240,13 +240,13 @@ access(all) contract Tax { // GOOD: Clear naming // -access(all) contract TaxUtilities { +pub contract TaxUtilities { // Clearly states what the field is for - access(all) var taxPercentage: UFix64 + pub var taxPercentage: UFix64 // Clearly states that this function calculates the // total cost after tax - access(all) fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { + pub fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { let postTaxCost = preTaxCost + (preTaxCost * self.taxPercentage) return postTaxCost @@ -254,9 +254,31 @@ access(all) contract TaxUtilities { } ``` +## 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 -For example, use `accounts` rather than `account` for an array of accounts. +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. @@ -277,17 +299,17 @@ It is usually safe to include post-conditions in transactions to verify the inte This could be used when purchasing an NFT to verify that the NFT was deposited in your account's collection. ```cadence -// Pseudo-code +// Psuedo-code transaction { - access(all) let buyerCollectionRef: &NonFungibleToken.Collection + pub let buyerCollectionRef: &NonFungibleToken.Collection - prepare(acct: auth(BorrowValue) &Account) { + 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.storage.borrow(from: /storage/Collection) + let self.buyerCollectionRef = acct.borrow(from: /storage/Collection) // purchase, supplying the buyers collection reference saleRef.purchase(tokenID: 1, recipient: self.buyerCollectionRef, buyTokens: <-temporaryVault) @@ -300,23 +322,20 @@ transaction { } ``` -## Avoid unnecessary load and save storage operations, prefer in-place mutations +## Avoid excessive load and save storage operations (prefer in-place mutations) ### Problem -When modifying data in account storage, `load()` and `save()` are costly operations: -All data is unnecessarily moved out of the account, then moved back into the account. -This can quickly cause your transaction to reach its limits. +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 nested, stored in fields, arrays, and dictionaries: -Moving objects out of containers and moving them back into the container, -just to modify the object, is just as costly. +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: -the dictionary can be updated in-place, which is easier and more efficient. +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. @@ -327,9 +346,6 @@ For making modifications to values in storage or accessing stored objects, `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. -Fields and value in containers, such as in arrays and dictionaries, -can be borrowed using a reference expression (`&v as &T`). - ### Example ```cadence @@ -337,10 +353,10 @@ can be borrowed using a reference expression (`&v as &T`). // transaction { - prepare(acct: auth(LoadValue, SaveValue) &Account) { + prepare(acct: AuthAccount) { // Removes the vault from storage, a costly operation - let vault <- acct.storage.load<@ExampleToken.Vault>(from: /storage/exampleToken) + let vault <- acct.load<@ExampleToken.Vault>(from: /storage/exampleToken) // Withdraws tokens let burnVault <- vault.withdraw(amount: 10) @@ -348,7 +364,7 @@ transaction { destroy burnVault // Saves the used vault back to storage, another costly operation - acct.storage.save(to: /storage/exampleToken) + acct.save(to: /storage/exampleToken) } } @@ -357,10 +373,10 @@ transaction { // transaction { - prepare(acct: auth(BorrowValue) &Account) { + prepare(acct: AuthAccount) { // Borrows a reference to the stored vault, much less costly operation - let vault <- acct.storage.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) + let vault <- acct.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) let burnVault <- vault.withdraw(amount: 10) @@ -373,12 +389,13 @@ transaction { # Capabilities -## Capability bootstrapping +## Capability Bootstrapping ### Problem -An account must be given a [capability](./language/capabilities.md) to an object stored in another account. -To create (issue) the capability, the transaction must be signed by a key which has access to the target account. +An account must be given a [capability](./language/capabilities.md) +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 @@ -389,7 +406,7 @@ from one account and delivering it to the other. ### Solution -The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](./language/accounts/inbox.mdx). +The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](./language/accounts.mdx#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. @@ -409,50 +426,67 @@ and emits an `InboxValueUnpublished` event that the recipient can listen for off 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 authorized in order to only give recipients access to the functionality +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 +## 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 issue a _new_ capability -and use it only for the purpose of granting the second account access. +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. -Once the first account wants to revoke access to the resource in storage, -they can simply get the capability controller for that capability and delete it. +**Account 1:** `/private/capability` → `/storage/resource` +`/private/revokableLink` -> `/private/capability` -## Check for existing capability before publishing new one +**Account 2:** `/storage/capability -> (Capability(→Account 1: /private/revokableLink))` -### Problem +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. -When publishing a capability, a capability might be already be published at the specified path. +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. -### Solution -Check if a capability is already published at the given path. +## 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: auth(Capabilities) &Account) { - let capability = signer.capabilities.storage - .issue<&ExampleToken.Vault>(/storage/exampleTokenVault) - - let publicPath = /public/exampleTokenReceiver - - if signer.capabilities.exits(publicPath) { - signer.capabilities.unpublish(publicPath) + 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.capabilities.publish(capability, at: publicPath) + + signer.link<&ExampleToken.Vault{FungibleToken.Receiver}>( + /public/exampleTokenReceiver, + target: /storage/exampleTokenVault + ) } } ``` diff --git a/docs/index.md b/docs/index.md index badebc2..144eec7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ title: Introduction to Cadence sidebar_position: 1 sidebar_label: Introduction --- - + In a blockchain environment like Flow, programs that are stored on-chain in accounts are commonly referred to as smart contracts. A smart contract is a program that verifies and executes the performance of a contract without the need for a trusted third party. Programs that run on blockchains are commonly referred to as smart contracts because they mediate important functionality (such as currency) diff --git a/docs/json-cadence-spec.md b/docs/json-cadence-spec.md index 5e059bb..eaaa1c7 100644 --- a/docs/json-cadence-spec.md +++ b/docs/json-cadence-spec.md @@ -403,7 +403,7 @@ Function values can only be exported, they cannot be imported. "value": { "functionType": { "kind": "Function", - "typeID": "fun():Void", + "typeID": "(():Void)", "parameters": [], "return": { "kind": "Void" @@ -729,13 +729,14 @@ Initializer types are encoded a list of parameters to the initializer. --- -## Intersection Types +## Restricted Types ```json { - "kind": "Intersection", + "kind": "Restriction", "typeID": "", - "types": [ + "type": , + "restrictions": [ , , //... diff --git a/docs/language/access-control.md b/docs/language/access-control.md index 8a020d3..7719ed8 100644 --- a/docs/language/access-control.md +++ b/docs/language/access-control.md @@ -3,111 +3,107 @@ title: Access control sidebar_position: 13 --- -Access control allows making certain parts of a program accessible/visible +Access control allows making certain parts of the program accessible/visible and making other parts inaccessible/invisible. -In Cadence, access control consists of: +In Flow and Cadence, there are two types of access control: -1. Access control on objects in account storage, - using [capability security](./capabilities.md). +1. Access control on objects in account storage using capability security. - A user is not able to access an object - unless they own the object or have a reference to that object. - This means that nothing is truly public by default. + Within Flow, a caller is not able to access an object + unless it owns the object or has a specific reference to that object. + This means that nothing is truly public by default. + Other accounts can not read or write the objects in an account + unless the owner of the account has granted them access + by providing references to the objects. - Other accounts can not read or write the objects in an account - unless the owner of the account has granted them access - by providing references to the objects. +2. Access control within contracts and objects + using `pub` and `access` keywords. - This kind of access control is covered in the pages - on [capabilities](./capabilities.md) - and [capability management](./accounts/capabilities.mdx). + For the explanations of the following keywords, we assume that + the defining type is either a contract, where capability security + doesn't apply, or that the caller would have valid access to the object + governed by capability security. -2. Access control within contracts and objects, - using access modifiers (`access` keyword). +The high-level reference-based security (point 1 above) +will be covered in a later section. -This page covers the second part of access control, -using access modifiers. +Top-level declarations +(variables, constants, functions, structures, resources, interfaces) +and fields (in structures, and resources) are always only able to be written +to and mutated (modified, such as by indexed assignment or methods like `append`) +in the scope where it is defined (self). -All declarations, such as [function](./functions.mdx), [composite types](./composite-types.mdx), and fields, -must be prefixed with an access modifier, using the `access` keyword. +There are four levels of access control defined in the code that specify where +a declaration can be accessed or called. -The access modifier determines where the declaration is accessible/visible. -Fields can only be assigned to and mutated from within the same or inner scope. +- **Public** or **access(all)** means the declaration + is accessible/visible in all scopes. -For example, to make a function publicly accessible (`access(all)` is explained below): - -``` -access(all) fun test() {} -``` - -There are five levels of access control: - -- **Public access** means the declaration is accessible/visible in all scopes. This includes the current scope, inner scopes, and the outer scopes. - A declaration is made publicly accessible using the `access(all)` modifier. - - For example, a public field in a type can be accessed + For example, a public field in a type can be accessed using the access syntax on an instance of the type in an outer scope. + This does not allow the declaration to be publicly writable though. -- **Entitled access** means the declaration is only accessible/visible - to the owner of the object, or to [references](./references.mdx) - that are authorized to the required [entitlements](#entitlements). + An element is made publicly accessible / by any code + by using the `pub` or `access(all)` keywords. - A declaration is made accessible through entitlements by using the `access(E)` syntax, - where `E` is a set of one or more entitlements, - or a single [entitlement mapping](#entitlement-mappings). +- **access(account)** means the declaration is only accessible/visible in the + scope of the entire account where it is defined. This means that + other contracts in the account are able to access it, - A reference is considered authorized to an entitlement - if that entitlement appears in the `auth` portion of the reference type. + An element is made accessible by code in the same account (e.g. other contracts) + by using the `access(account)` keyword. - For example, an `access(E, F)` field on a resource `R` can only be accessed by an owned (`@R`-typed) value, - or a reference to `R` that is authorized to the `E` and `F` entitlements (`auth(E, F) &R`). - -- **Account access** means the declaration is only accessible/visible - in the scope of the entire account where it is defined. - This means that other contracts in the account are able to access it. - - A declaration is made accessible by code in the same account, - for example other contracts, by using the `access(account)` keyword. - -- **Contract access** means the declaration is only accessible/visible - in the scope of the contract that defined it. - This means that other declarations that are defined in the same contract can access it, +- **access(contract)** means the declaration is only accessible/visible in the + scope of the contract that defined it. This means that other types + and functions that are defined in the same contract can access it, but not other contracts in the same account. - A declaration is made accessible by code in the same contract + An element is made accessible by code in the same contract by using the `access(contract)` keyword. -- **Private access** means the declaration is only accessible/visible +- Private or **access(self)** means the declaration is only accessible/visible in the current and inner scopes. - A declaration is made accessible by code in the same containing type + For example, an `access(self)` field can only be + accessed by functions of the type is part of, + not by code in an outer scope. + + An element is made accessible by code in the same containing type by using the `access(self)` keyword. - For example, an `access(self)` field can only be accessed - by functions of the type is part of, not by code in an outer scope. +**Access level must be specified for each declaration** + +The `(set)` suffix can be used to make variables also publicly writable and mutable. To summarize the behavior for variable declarations, constant declarations, and fields: -| Declaration kind | Access modifier | Accessible in | Assignable in | Mutable in | -|:-----------------|:-------------------|:-----------------------------------------------------|:------------------|:------------------| -| `let` | `access(self)` | Current and inner | *None* | Current and inner | -| `let` | `access(contract)` | Current, inner, and containing contract | *None* | Current and inner | -| `let` | `access(account)` | Current, inner, and other contracts in same account | *None* | Current and inner | -| `let` | `access(all)` | **All** | *None* | Current and inner | -| `let` | `access(E)` | **All** with required entitlements | *None* | Current and inner | -| `var` | `access(self)` | Current and inner | Current and inner | Current and inner | -| `var` | `access(contract)` | Current, inner, and containing contract | Current and inner | Current and inner | -| `var` | `access(account)` | Current, inner, and other contracts in same account | Current and inner | Current and inner | -| `var` | `access(all)` | **All** | Current and inner | Current and inner | -| `var` | `access(E)` | **All** with required entitlements | Current and inner | Current and inner | - -Declarations of [composite types](./composite-types.mdx) must be public. +| Declaration kind | Access modifier | Read scope | Write scope | Mutate scope | +|:-----------------|:-------------------------|:-----------------------------------------------------|:------------------|:------------------| +| `let` | `priv` / `access(self)` | Current and inner | *None* | Current and inner | +| `let` | `access(contract)` | Current, inner, and containing contract | *None* | Current and inner | +| `let` | `access(account)` | Current, inner, and other contracts in same account | *None* | Current and inner | +| `let` | `pub`,`access(all)` | **All** | *None* | Current and inner | +| `var` | `access(self)` | Current and inner | Current and inner | Current and inner | +| `var` | `access(contract)` | Current, inner, and containing contract | Current and inner | Current and inner | +| `var` | `access(account)` | Current, inner, and other contracts in same account | Current and inner | Current and inner | +| `var` | `pub` / `access(all)` | **All** | Current and inner | Current and inner | +| `var` | `pub(set)` | **All** | **All** | **All** | + +To summarize the behavior for functions: + +| Access modifier | Access scope | +|:-------------------------|:----------------------------------------------------| +| `priv` / `access(self)` | Current and inner | +| `access(contract)` | Current, inner, and containing contract | +| `access(account)` | Current, inner, and other contracts in same account | +| `pub` / `access(all)` | **All** | + +Declarations of structures, resources, events, and [contracts](./contracts.mdx) can only be public. However, even though the declarations/types are publicly visible, -resources can only be created, and events can only be emitted -from inside the contract they are declared in. +resources can only be created from inside the contract they are declared in. ```cadence // Declare a private constant, inaccessible/invisible in outer scope. @@ -116,13 +112,13 @@ access(self) let a = 1 // Declare a public constant, accessible/visible in all scopes. // -access(all) let b = 2 +pub let b = 2 ``` ```cadence // Declare a public struct, accessible/visible in all scopes. // -access(all) struct SomeStruct { +pub struct SomeStruct { // Declare a private constant field which is only readable // in the current and inner scopes. @@ -131,7 +127,7 @@ access(all) struct SomeStruct { // Declare a public constant field which is readable in all scopes. // - access(all) let b: Int + pub let b: Int // Declare a private variable field which is only readable // and writable in the current and inner scopes. @@ -142,11 +138,16 @@ access(all) struct SomeStruct { // so it is only writable in the current and inner scopes, // and readable in all scopes. // - access(all) var d: Int + pub var d: Int + + // Declare a public variable field which is settable, + // so it is readable and writable in all scopes. + // + pub(set) var e: Int // Arrays and dictionaries declared without (set) cannot be // mutated in external scopes - access(all) let arr: [Int] + pub let arr: [Int] // The initializer is omitted for brevity. @@ -159,7 +160,7 @@ access(all) struct SomeStruct { // Declare a public function which is callable in all scopes. // - access(all) fun publicTest() { + pub fun privateTest() { // ... } @@ -201,6 +202,14 @@ some.d // some.d = 4 +// Valid: can read publicly settable variable field in outer scope. +// +some.e + +// Valid: can set publicly settable variable field in outer scope. +// +some.e = 5 + // Invalid: cannot mutate a public field in outer scope. // some.f.append(0) @@ -212,399 +221,3 @@ some.f[3] = 1 // Valid: can call non-mutating methods on a public field in outer scope some.f.contains(0) ``` - -## Entitlements - -Entitlements provide granular access control to each member of a composite. -Entitlements are declared using the syntax `entitlement E`, -where `E` is the name of the entitlement. - -For example, the following code declares two entitlements called `E` and `F`: - -```cadence -entitlement E -entitlement F -``` - -Entitlements can be imported from other contracts and used the same way as other types. -When using entitlements defined in another contract, the same qualified name syntax is used as for other types: - -```cadence -contract C { - entitlement E -} -``` - -Outside of `C`, `E` is used with `C.E` syntax. - -Entitlements exist in the same namespace as types, so if a contract declares a resource called `R`, -it is impossible to declare an entitlement that is also called `R`. - -Entitlements can be used in access modifiers composite members (fields and functions) -to specify which references to those composites are allowed to access those members. - -An access modifier can include more than one entitlement, -joined with either an `|`, to indicate disjunction ("or"), -or a `,`, to indicate conjunction ("and"). -The two kinds of separators cannot be combined in the same set. - -For example: - -```cadence -access(all) -resource SomeResource { - - // requires a reference to have an `E` entitlement to read this field - access(E) - let a: Int - - // requires a reference to have either an `E` OR an `F` entitlement to read this field. - access(E | F) - let b: Int - - // requires a reference to have both an `E` AND an `F` entitlement to read this field - access(E, F) - let b: Int - - // intializers omitted for brevity - // ... -} -``` - -Assuming the following constants exists, -which have owned or [reference](./references.mdx) types: - -```cadence -let r: @SomeResource = // ... -let refE: auth(E) &SomeResource = // ... -let refF: auth(F) &SomeResource = // ... -let refEF: auth(E, F) &SomeResource = // ... -``` - -The references can be used as follows: - -```cadence -// valid, because `r` is owned and thus is "fully entitled" -r.a -// valid, because `r` is owned and thus is "fully entitled" -r.b -// valid, because `r` is owned and thus is "fully entitled" -r.c - -// valid, because `refE` has an `E` entitlement as required -refE.a -// valid, because `refE` has one of the two required entitlements -refE.b -// invalid, because `refE` only has one of the two required entitlements -refE.c - -// invalid, because `refF` has an `E` entitlement, not an `F` -refF.a -// valid, because `refF` has one of the two required entitlements -refF.b -// invalid, because `refF` only has one of the two required entitlements -refF.c - -// valid, because `refEF` has an `E` entitlement -refEF.a -// valid, because `refEF` has both of the two required entitlements -refEF.b -// valid, because `refEF` has both of the two required entitlements -refEF.c -``` - -Note particularly in this example how the owned value `r` can access all entitled members on `SomeResource`. -Owned values are not affected by entitled declarations. - -### Entitlement mappings - -Entitlement mappings are a way to statically declare how entitlements are propagated -from parents to child objects in a nesting hierarchy. - -When objects have fields that are child objects, -to grant access to the inner object based on the entitlements of the reference to the parent object. - -Consider the following example, -which uses entitlements to control access to an inner resource: - -```cadence -entitlement OuterEntitlement -entitlement InnerEntitlement - -resource InnerResource { - access(all) - fun foo() { ... } - - access(InnerEntitlement) - fun bar() { ... } -} - -resource OuterResource { - access(self) - let childResource: @InnerResource - - init(childResource: @InnerResource) { - self.childResource <- childResource - } - - // The parent resource has to provide two accessor functions - // which return a reference to the inner resource. - // - // If the reference to the outer resource is unauthorized - // and does not have the OuterEntitlement entitlement, - // the outer resource allows getting an unauthorized reference - // to the inner resource. - // - // If the reference to the outer resource is authorized - // and it has the OuterEntitlement entitlement, - // the outer resource allows getting an authorized reference - // to the inner resource. - - access(all) - fun getPubRef(): &InnerResource { - return &self.childResource as &InnerResource - } - - access(OuterEntitlement) - fun getEntitledRef(): auth(InnerEntitlement) &InnerResource { - return &self.childResource as auth(InnerEntitlement) &InnerResource - } -} -``` - -With this pattern, it is possible to store a `InnerResource` in an `OuterResource`, -and create different ways to access that nested resource depending on the entitlement one possesses. - -An unauthorized reference to `OuterResource` can only be used to call the `getPubRef` function, -and thus can only obtain an unauthorized reference to `InnerResource`. -That reference to the `InnerResource` then only allows calling the function `foo`, which is publicly accessible, -but not function `bar`, as it needs the `InnerEntitlement` entitlement, which is not granted. - -However a `OuterEntitlement`-authorized reference to the `OuterResource` can be used to call the `getEntitledRef` function, -which returns a `InnerEntitlement`-authorized reference to `InnerResource`, -which in turn can be used to call function `bar`. - -This pattern is functional, but it is unfortunate that the accessor functions to `InnerResource` have to be "duplicated". - -To avoid necessitating this duplication, entitlement mappings can be used. - -Entitlement mappings are declared using the syntax: - -```cadence -entitlement mapping M { - // ... -} -``` - -Where `M` is the name of the mapping. - -The body of the mapping contains zero or more rules of the form `A -> B`, -where `A` and `B` are entitlements. -Each rule defines that, given a reference with the entitlement on the left, -a reference with the entitlement on the right is returned. - -An entitlement mapping thus defines a function from an input set of entitlements (called the domain) -to an output set (called the range or the image). - -Using entitlement mappings, the above example could be equivalently written as: - -```cadence -entitlement OuterEntitlement -entitlement InnerEntitlement - -// Specify a mapping for entitlements called `OuterToInnerMap`, -// which maps the entitlement `OuterEntitlement` to the entitlement `InnerEntitlement`. -entitlement mapping OuterToInnerMap { - OuterEntitlement -> InnerEntitlement -} - -resource InnerResource { - access(all) - fun foo() { ... } - - access(InnerEntitlement) - fun bar() { ... } -} - -resource OuterResource { - // Use the entitlement mapping `OuterToInnerMap`. - // - // This declares that when the field `childResource` is accessed - // using a reference authorized with the entitlement `OuterEntitlement`, - // then a reference with the entitlement `InnerEntitlement` is returned. - // - // This is equivalent to the two accessor functions - // that were necessary in the previous example. - // - access(OuterToInnerMap) - let childResource: @InnerResource - - init(childResource: @InnerResource) { - self.childResource <- childResource - } - - // No accessor functions are needed. -} - -// given some value `r` of type `@OuterResource` - -let pubRef = &r as &OuterResource -let pubInnerRef = pubRef.childResource // has type `&InnerResource` - -let entitledRef = &r as auth(OuterEntitlement) &OuterResource -let entitledInnerRef = entitledRef.childResource // has type `auth(InnerEntitlement) &InnerResource`, - // as `OuterEntitlement` is defined to map to `InnerEntitlement`. - -// `r` is an owned value, and is thus considered "fully-entitled" to `OuterResource`, -// so this access yields a value authorized to the entire image of `OuterToInnerMap`, -// in this case `InnerEntitlement` -let alsoEntitledInnerRef = r.childResource -``` - -Entitlement mappings can be used either in accessor functions (as in the example above), -or in fields whose types are either references, or containers (composite types, dictionaries, and arrays). - -Entitlement mappings need not be 1:1. -It is valid to define a mapping where many inputs map to the same output, -or where one input maps to many outputs. - -Entitlement mappings preserve the "kind" of the set they are mapping. -That is, mapping a conjunction ("and") set produces a conjunction set, -and mapping a disjunction ("or") set produces a disjunction set. - -Because entitlement separators cannot be combined in the same set, -attempting to map disjunction ("or") sets through certain complex mappings can result in a type error. - -For example, given the following entitlement mapping: - -```cadence -entitlement mapping M { - A -> B - A -> C - D -> E -} -``` - -Attempting to map `(A | D)` through `M` will fail, -since `A` should map to `(B, C)` and `D` should map to `E`, -but these two outputs cannot be combined into a disjunction ("or") set. - -A good example for how entitlement mappings can be used is the [`Account` type](./accounts/index.mdx). - -### The `Identity` entitlement mapping - -`Identity` is a built-in entitlement mapping that maps every input to itself as the output. -Any entitlement set passed through the `Identity` map will come out unchanged in the output. - -For instance: - -```cadence -entitlement X - -resource InnerResource { - // ... -} - -resource OuterResource { - access(Identity) - let childResource: @InnerResource - - access(Identity) - getChildResource(): auth(Identity) &InnerResource { - return &self.childResource - } - - init(childResource: @InnerResource) { - self.childResource <- childResource - } -} - -fun example(outerRef: auth(X) &OuterResource) { - let innerRef = outerRef.childResource // `innerRef` has type `auth(X) &InnerResource`, - // as `outerRef` was authorized with entitlement `X` -} -``` - -One important point to note about the `Identity` mapping, however, -is that its full output range is unknown, and theoretically infinite. -Because of that, -accessing an `Identity`-mapped field or function with an owned value will yield an empty output set. - -For example, calling `getChildResource()` on an owned `OuterResource` value, -will produce an unauthorized `&InnerResource` reference. - -### Mapping composition - -Entitlement mappings can be composed. -In the definition of an entitlement mapping, -it is possible to include the definition of one or more other mappings, -to copy over their mapping relations. - -An entitlement mapping is included into another entitlement mapping using the `include M` syntax, -where `M` is the name of the entitlement mapping to be included. - -In general, an `include M` statement in the definition of an entitlement mapping `N` -is equivalent to simply copy-pasting all the relations defined in `M` into `N`'s definition. - -Support for `include` is provided primarily to reduce code-reuse and promote composition. - -For example: - -```cadence -entitlement mapping M { - X -> Y - Y -> Z -} - -entitlement mapping N { - E -> F -} - -entitlement mapping P { - include M - include N - F -> G -} -``` - -The entitlement mapping `P` includes all of the relations defined in `M` and `N`, -along with the additional relations defined in its own definition. - -It is also possible to include the `Identity` mapping. -Any mapping `M` that includes the `Identity` mapping will map its input set to itself, -along with any additional relations defined in the mapping, -or in other included mappings. - -For instance: - -```cadence -entitlement mapping M { - include Identity - X -> Y -} -``` - -The mapping `M` maps the entitlement set `(X)` to `(X, Y)`, -and `(Y)` to `(Y)`. - -Includes that produce a cyclical mapping are rejected by the type-checker. - -### Built-in mutability entitlements - -A prominent use-case of entitlements is to control access to object based on mutability. - -For example, in a composite, the author would want to control the access to certain fields to be read-only, -and some fields to be mutable, etc. - -In order to support this, the following built-in entitlements can be used: -- `Insert` -- `Remove` -- `Mutate` - -These are primarily used by the built-in [array](./values-and-types.mdx#arrays) -and [dictionary](./values-and-types.mdx#dictionaries) functions, -but are available to be used in access modifiers of any declaration. - -While Cadence does not support entitlement composition or inheritance, -the `Mutate` entitlement is intended to be used as an equivalent form -to the conjunction of the `(Insert, Remove)` entitlement set. diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts.mdx b/docs/language/accounts.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts.mdx rename to docs/language/accounts.mdx diff --git a/docs/language/accounts/index.mdx b/docs/language/accounts/index.mdx index 337fee6..c85e77b 100644 --- a/docs/language/accounts/index.mdx +++ b/docs/language/accounts/index.mdx @@ -3,8 +3,8 @@ title: Accounts --- The type `Account` provides access to accounts, -Accounts are only accessed through [references](../references.mdx), -which might be [authorized](../references.mdx#authorized-references). +Accounts are only accessed through [references](../references.md), +which might be [authorized](../references.md#authorized-references). Account objects provide information about and allow the management of different aspects of the account, such as [account storage](./storage.mdx), diff --git a/docs/language/accounts/paths.mdx b/docs/language/accounts/paths.mdx index 2a3bae5..c81e87e 100644 --- a/docs/language/accounts/paths.mdx +++ b/docs/language/accounts/paths.mdx @@ -7,7 +7,7 @@ Account storage stores objects under paths. Paths consist of a domain and an identifier. Paths start with the character `/`, followed by the domain, the path separator `/`, -and finally the identifier. The identifier must start with a letter and can only be followed by letters, numbers, or the underscore `_`. +and finally the identifier. For example, the path `/storage/test` has the domain `storage` and the identifier `test`. There are two valid domains: `storage` and `public`. diff --git a/docs/language/attachments.mdx b/docs/language/attachments.mdx index 86f4823..e4fb948 100644 --- a/docs/language/attachments.mdx +++ b/docs/language/attachments.mdx @@ -14,11 +14,11 @@ without requiring the original author of the type to plan or account for the int ## Declaring Attachments Attachments are declared with the `attachment` keyword, which would be declared using a new form of composite declaration: -`attachment for : { ... }`, where the attachment functions and fields are declared in the body. +`pub attachment for : { ... }`, where the attachment functions and fields are declared in the body. As such, the following would be examples of legal declarations of attachments: ```cadence -access(all) attachment Foo for MyStruct { +pub attachment Foo for MyStruct { // ... } @@ -36,25 +36,16 @@ Note that the base type may be either a concrete composite type or an interface. In the former case, the attachment is only usable on values specifically of that base type, while in the case of an interface the attachment is usable on any type that conforms to that interface. -Unlike other type declarations, attachments may use either an `access(all)` access modifier, or an `access(M)` modifier, -where `M` is the name of an entitlement mapping. -When attachments are defined with an `access(all)` modifier, -members on the attachment may not use any entitlements in their access modifiers, -and any references to that attachment are always unauthorized. -When attachments are defined with an an [entitlement mapping](./access-control.md), -members on the attachments may use any entitlements in the range of that mapping, -and any references to that attachments will have their authorization depend on the entitlements present on the base type on which they are accessed. +As with other type declarations, attachments may only have a `pub` access modifier (if one is present). The body of the attachment follows the same declaration rules as composites. In particular, they may have both field and function members, -and any field members must be initialized in an initializer. +and any field members must be initialized in an `init` function. Only resource-kinded attachments may have resource members, and such members must be explicitly handled in the `destroy` function. The `self` keyword is available in attachment bodies, but unlike in a composite, `self` is a **reference** type, rather than a composite type: In an attachment declaration for `A`, the type of `self` would be `&A`, rather than `A` like in other composite declarations. -If the attachment declaration uses an `access(all)` access modifier, the `self` reference is always unauthorized, -whereas if it uses an `access(M)` access modifier, the `self` reference is fully-entitled to the range of `M`. If a resource with attachments on it is `destroy`ed, the `destroy` functions of all its attachments are all run in an unspecified order; `destroy` should not rely on the presence of other attachments on the base type in its implementation. @@ -64,29 +55,29 @@ Within the body of an attachment, there is also a `base` keyword available, which contains a reference to the attachment's base value; that is, the composite to which the attachment is attached. Its type, therefore, is a reference to the attachment's declared base type. -So, for an attachment declared `access(all) attachment Foo for Bar`, the `base` field of `Foo` would have type `&Bar`. +So, for an attachment declared `pub attachment Foo for Bar`, the `base` field of `Foo` would have type `&Bar`. So, for example, this would be a valid declaration of an attachment: ``` -access(all) resource R { - access(all) let x: Int +pub resource R { + pub let x: Int init (_ x: Int) { self.x = x } - access(all) fun foo() { ... } + pub fun foo() { ... } } -access(all) attachment A for R { - access(all) let derivedX: Int +pub attachment A for R { + pub let derivedX: Int init (_ scalar: Int) { self.derivedX = base.x * scalar } - access(all) fun foo() { + pub fun foo() { base.foo() } } @@ -95,18 +86,18 @@ access(all) attachment A for R { For the purposes of external mutation checks or [access control](./access-control.md), the attachment is considered a separate declaration from its base type. -A developer cannot, therefore, access any `access(self)` fields +A developer cannot, therefore, access any `priv` fields (or `access(contract)` fields if the base was defined in a different contract to the attachment) on the `base` value, nor can they mutate any array or dictionary typed fields. ```cadence -access(all) resource interface SomeInterface { - access(all) let b: Bool - access(self) let i: Int - access(all) let a: [String] +pub resource interface SomeInterface { + pub let b: Bool + priv let i: Int + pub let a: [String] } -access(all) attachment SomeAttachment for SomeContract.SomeStruct { - access(all) let i: Int +pub attachment SomeAttachment for SomeContract.SomeStruct { + pub let i: Int init(i: Int) { if base.b { self.i = base.i // cannot access `i` on the `base` value @@ -114,65 +105,38 @@ access(all) attachment SomeAttachment for SomeContract.SomeStruct { self.i = i } } - access(all) fun foo() { + pub fun foo() { base.a.append("hello") // cannot mutate `a` outside of the composite where it was defined } } ``` -By default, the `base` reference is unauthorized, and thus entitled-access members on the base type are inaccessible in the attachment. -If the author of the attachment wishes to have access to entitled-access members on the base type, -they must declare that their attachment requires certain entitlements to the base, using `require entitlement E` syntax. -Required entitlements must be valid entitlements for the base type, -and requiring entitlements in the attachment declaration will impose additional requirements when the attachment is attached, -as described below. So, for example: - -```cadence -entitlement mapping M { - E -> F -} - -resource R { - access(E) fun foo() { - //... - } -} - -access(M) attachment A for R { - require entitlement E - - access(all) fun bar() { - base.foo() // available because `E` is required above, and thus `base` is type `auth(E) &R`. - } -} -``` - ### Attachment Types -An attachment declared with `access(all) attachment A for C { ... }` will have a nominal type `A`. +An attachment declared with `pub attachment A for C { ... }` will have a nominal type `A`. -It is important to note that attachments are not first class values, and as such their usage is limited in certain ways. +It is important to note that attachments are not first class values in Cadence, and as such their usage is limited in certain ways. In particular, their types cannot appear outside of a reference type. -So, for example, given an attachment declaration `attachment A for X {}`, the types `A`, `A?`, `[A]` and `fun(): A` are not valid type annotations, -while `&A`, `&A?`, `[&A]` and `fun(): &A` are valid. +So, for example, given an attachment declaration `attachment A for X {}`, the types `A`, `A?`, `[A]` and `((): A)` are not valid type annotations, +while `&A`, `&A?`, `[&A]` and `((): &A)` are valid. ## Creating Attachments An attachment is created using an `attach` expression, where the attachment is both initialized and attached to the base value in a single operation. -Attachments are not first-class values; they cannot exist independently of a base value, +Attachments are not first-class values in Cadence; they cannot exist independently of a base value, nor can they be moved around on their own. This means that an `attach` expression is the only place in which an attachment constructor can be called. Tightly coupling the creation and attaching of attachment values helps to make reasoning about attachments simpler for the user. -Also for this reason, resource attachments do not need an explicit `<-` move operator when they appear in an `attach` expression. +Also for this reason, resource attachments do not need an expliict `<-` move operator when they appear in an `attach` expression. An attach expression consists of the `attach` keyword, a constructor call for the attachment value, the `to` keyword, and an expression that evaluates to the base value for that attachment. -Any arguments required by the attachment's initializer are provided in its constructor call. +Any arguments required by the attachment's `init` function are provided in its constructor call. ```cadence -access(all) resource R {} -access(all) attachment A for R { +pub resource R {} +pub attachment A for R { init(x: Int) { //... } @@ -185,15 +149,15 @@ let r2 <- attach A(x: 3) to <-r The expression on the right-hand side of the `to` keyword must evaluate to a composite value whose type is a subtype of the attachment's base, and is evaluated before the call to the constructor on the left side of `to`. -This means that the `base` value is available inside of the attachment's initializer, +This means that the `base` value is available inside of the attachment's `init` function, but it is important to note that the attachment being created will not be accessible on the `base` (see the accessing attachments section below) until after the constructor finishes executing. ```cadence -access(all) resource interface I {} -access(all) resource R: I {} -access(all) attachment A for I {} +pub resource interface I {} +pub resource R: I {} +pub attachment A for I {} // ... let r <- create R() // has type @R @@ -205,14 +169,6 @@ Cadence will raise a runtime error if a user attempts to add an attachment to a The type returned by the `attach` expression is the same type as the expression on the right-hand side of the `to`; attaching an attachment to a value does not change its type. -If an attachment has required entitlements to its base, those entitlements must be explicitly provided in the `attach` expression -using an additional `with` syntax. So, for example, if an attachment `A` declared for `R` requires entitlements `E` and `F`, it can -be attached to an `r` of type `@R` like so: - -```cadence -let rWithA <- attach A() to <-r with (E, F) -``` - ## Accessing Attachments Attachments are accessed on composites via type-indexing: @@ -225,12 +181,12 @@ let a = v[A] // has type `&A?` This syntax requires that `A` is a nominal attachment type, and that `v` has a composite type that is a subtype of `A`'s declared base type. -As mentioned above, attachments are not first-class values, +As mentioned above, attachments are not first-class values in Cadence, so this indexing returns a reference to the attachment on `v`, rather than the attachment itself. If the attachment with the given type does not exist on `v`, this expression returns `nil`. Because the indexed value must be a subtype of the indexing attachment's base type, -the owner of a resource can restrict which attachments can be accessed on references to their resource using interface types, +the owner of a resource can restrict which attachments can be accessed on references to their resource using restricted types, much like they would do with any other field or function. E.g. ```cadence @@ -245,31 +201,6 @@ fun foo(r: &{I}) { Hence, if the owner of a resource wishes to allow others to use a subset of its attachments, they can create a capability to that resource with a borrow type that only allows certain attachments to be accessed. -If an attachment is declared with an `access(all)` modifier, -accessing one this way will always produce an unauthorized reference to the attachment. -However, if the attachment is declared with an `access(M)` modifier, where `M` is some entitlement mapping, -then the authorization of the resulting reference will depend on the authorization of the accessed value. - -So, for example, given a declaration - -```cadence -entitlement E -entitlement F -entitlement G -entitlement H -entitlement mapping M { - E -> F - G -> H -} -resource R {} -access(M) attachment A for R {} -``` - -when `A` is accessed on an owned value of type `@R`, it will be fully-authorized to the domain of `M`, -having a type of `auth(F, H) &A`. -However, if `A` is accessed on an `auth(E) &R` reference, then it will only have a `auth(F) &A` type. -If `A` is accessed on an unauthorized `&R` reference, then it will yield an unauthorized `&A` type. - ## Removing Attachments Attachments can be removed from a value with a `remove` statement. diff --git a/docs/language/built-in-functions.mdx b/docs/language/built-in-functions.mdx index 375ebc7..82c61fc 100644 --- a/docs/language/built-in-functions.mdx +++ b/docs/language/built-in-functions.mdx @@ -3,93 +3,88 @@ title: Built-in Functions sidebar_position: 28 --- -## `panic` +## panic # ```cadence fun panic(_ message: String): Never ``` -Terminates the program unconditionally -and reports a message which explains why the unrecoverable error occurred. + Terminates the program unconditionally + and reports a message which explains why the unrecoverable error occurred. -```cadence -panic("something went wrong: ...") -``` + ```cadence + let optionalAccount: AuthAccount? = // ... + let account = optionalAccount ?? panic("missing account") + ``` -## `assert` +## assert ```cadence fun assert(_ condition: Bool, message: String) ``` -Terminates the program if the given condition is false, -and reports a message which explains how the condition is false. -Use this function for internal sanity checks. + Terminates the program if the given condition is false, + and reports a message which explains how the condition is false. + Use this function for internal sanity checks. -The message argument is optional. + The message argument is optional. -## `unsafeRandom` +## unsafeRandom ```cadence fun unsafeRandom(): UInt64 ``` -Returns a pseudo-random number. + Returns a pseudo-random number. - -Smart contract developers should be mindful about the limitations of unsafeRandom. -The stream of random numbers produced is potentially unsafe in the following two regards: + NOTE: + Smart contract developers should be mindful about the limitations of unsafeRandom. + The stream of random numbers produced is potentially unsafe in the following two regards: -1. The sequence of random numbers is potentially predictable by transactions within the same block -and by other smart contracts calling into your smart contract. -2. A transaction calling into your smart contract can potentially bias the sequence of random numbers which -your smart contract internally generates. + 1. The sequence of random numbers is potentially predictable by transactions within the same block + and by other smart contracts calling into your smart contract. + 2. A transaction calling into your smart contract can potentially bias the sequence of random numbers which + your smart contract internally generates. -The Flow project is working towards removing these limitations incrementally. -Once Flow addressed these points and randomness is safe, -the "unsafe" qualifier is going to get removed. + We are working towards removing these limitations incrementally. Once these points are addressed, + Flow’s randomness is safe and we will remove the "unsafe" qualifier. -Nevertheless, there is an additional safety-relevant aspect that developers need to be mindful about: + Nevertheless, there is an additional safety-relevant aspect that developers need to be mindful about: -A transaction can atomically revert all its actions at any time. -Therefore, it is possible for a transaction calling into a smart contract -to post-select favorable results and revert the transaction for unfavorable results. + * A transaction can atomically revert all its action at any time. Therefore, it is possible for a transaction calling into + your smart contract to post-select favourable results and revert the transaction for unfavourable results. + ([example](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/)) -This limitation is inherent to any smart contract platform that allows transactions to roll back atomically -and cannot be solved through safe randomness alone. -Providing additional Cadence language primitives to simplify this challenge for developers is on the roadmap. -Nevertheless, with safe randomness, points 1 and 2 above resolved, -developers can prevent clients from post-select favorable outcomes using approaches such as described in the -[Smart Contract Security Best Practices](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/). - + This limitation is inherent to any smart contract platform that allows transactions to roll back atomically and cannot be + solved through safe randomness alone. Providing additional Cadence language primitives to simplify this challenge for + developers is on our roadmap as well. Nevertheless, with safe randomness (points 1 and 2 above resolved), developers can prevent + clients from post-select favourable outcomes using approaches such as described in the + [example](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/). -## `RLP` -Recursive Length Prefix (RLP) serialization allows the encoding of arbitrarily nested arrays of binary data. +## RLP -Cadence provides RLP decoding functions in the built-in `RLP` contract, which does not need to be imported. +RLP (Recursive Length Prefix) serialization allows the encoding of arbitrarily nested arrays of binary data. -### `decodeString` +Cadence provides RLP decoding functions in the built-in `RLP` contract, which does not need to be imported. -```cadence -fun decodeString(_ input: [UInt8]): [UInt8] -``` +- + ```cadence + fun decodeString(_ input: [UInt8]): [UInt8] + ``` -Decodes an RLP-encoded byte array. RLP calls this a "string." -The byte array should only contain of a single encoded value for a string. -If the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. -If the function encounters any error while decoding, the program aborts. + Decodes an RLP-encoded byte array (called string in the context of RLP). + The byte array should only contain of a single encoded value for a string; if the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. + If any error is encountered while decoding, the program aborts. -### `decodeList` -```cadence -fun decodeList(_ input: [UInt8]): [[UInt8]]` -``` -Decodes an RLP-encoded list into an array of RLP-encoded items. + ```cadence + fun decodeList(_ input: [UInt8]): [[UInt8]]` + ``` -Note that this function does not recursively decode, so each element of the resulting array is RLP-encoded data. -The byte array should only contain of a single encoded value for a list. -If the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. -If the function encounters any error while decoding, the program aborts. + Decodes an RLP-encoded list into an array of RLP-encoded items. + Note that this function does not recursively decode, so each element of the resulting array is RLP-encoded data. + The byte array should only contain of a single encoded value for a list; if the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. + If any error is encountered while decoding, the program aborts. diff --git a/docs/language/capabilities.md b/docs/language/capabilities.md index f001a3d..8f4da8c 100644 --- a/docs/language/capabilities.md +++ b/docs/language/capabilities.md @@ -1,70 +1,257 @@ --- -title: Capabilities -sidebar_position: 14 +title: Capability-based Access Control +sidebar_position: 20 --- -Cadence supports [capability-based security](https://en.wikipedia.org/wiki/Capability-based_security) -through the [object-capability model](https://en.wikipedia.org/wiki/Object-capability_model). +Users will often want to make it so that specific other users or even anyone else +can access certain fields and functions of a stored object. +This can be done by creating a capability. -A capability in Cadence is a value that represents the right -to access an object and perform certain operations on it. -A capability specifies _what_ can be accessed, and _how_ it can be accessed. +As was mentioned before, access to stored objects is governed by the +tenets of [Capability Security](https://en.wikipedia.org/wiki/Capability-based_security). +This means that if an account wants to be able to access another account's +stored objects, it must have a valid capability to that object. -Capabilities are unforgeable, transferable, and revocable. +Capabilities are identified by a path and link to a target path, not directly to an object. +Capabilities are either public (any user can get access), +or private (access to/from the authorized user is necessary). -Capabilities can be storage capabilities or account capabilities: -- **Storage capabilities** grant access to [objects in account storage](./accounts/storage.mdx), -via [paths](./accounts/paths.mdx) -- **Account capabilities** grant access to [accounts](./accounts/index.mdx) +Public capabilities are created using public paths, i.e. they have the domain `public`. +After creation they can be obtained from both authorized accounts (`AuthAccount`) +and public accounts (`PublicAccount`). -Capabilities can be borrowed to get a [reference](./references.mdx) to the stored object or the account it refers to. +Private capabilities are created using private paths, i.e. they have the domain `private`. +After creation they can be obtained from authorized accounts (`AuthAccount`), +but not from public accounts (`PublicAccount`). -Capabilities have the type `Capability`. -The type parameter specifies the kind of reference that can be obtained when borrowing the capability. -The type specifies the associated set of access rights through [entitlements](./access-control.md): -the reference type of the capability can be authorized, -which grants the owner of the capability the ability to access the fields and functions of the target -which require the given entitlements. +Once a capability is created and obtained, it can be borrowed to get a reference +to the stored object. +When a capability is created, a type is specified that determines as what type +the capability can be borrowed. +This allows exposing and hiding certain functionality of a stored object. -For example, a capability which has type `Capability` -grants access to an account, and allows saving a value into the account. +Capabilities are created using the `link` function of an authorized account (`AuthAccount`): -Each capability has an ID. -The ID is unique **per account/address**. +- + ```cadence + fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? + ``` -Capabilities are created and managed through [capability controllers](./accounts/capabilities.mdx). + `newCapabilityPath` is the public or private path identifying the new capability. -## `Capability` + `target` is any public, private, or storage path that leads to the object + that will provide the functionality defined by this capability. + + `T` is the type parameter for the capability type. + A type argument for the parameter must be provided explicitly. + + The type parameter defines how the capability can be borrowed, + i.e., how the stored value can be accessed. + + The link function returns `nil` if a link for the given capability path already exists, + or the newly created capability if not. + + It is not necessary for the target path to lead to a valid object; + the target path could be empty, or could lead to an object + which does not provide the necessary type interface: + + 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. + + The link is latent. + The target value might be stored after the link is created, + and the target value might be moved out after the link has been created. + +Capabilities can be removed using the `unlink` function of an authorized account (`AuthAccount`): + +- + ```cadence + fun unlink(_ path: CapabilityPath) + ``` + + `path` is the public or private path identifying the capability that should be removed. + +To get the target path for a capability, the `getLinkTarget` function +of an authorized account (`AuthAccount`) or public account (`PublicAccount`) can be used: + + +- + ```cadence + fun getLinkTarget(_ path: CapabilityPath): Path? + ``` + + `path` is the public or private path identifying the capability. + The function returns the link target path, + if a capability exists at the given path, + or `nil` if it does not. + +Existing capabilities can be obtained by using the `getCapability` function +of authorized accounts (`AuthAccount`) and public accounts (`PublicAccount`): + +- + ```cadence + fun getCapability(_ at: CapabilityPath): Capability + ``` + + For public accounts, the function returns a capability + if the given path is public. + It is not possible to obtain private capabilities from public accounts. + If the path is private or a storage path, the function returns `nil`. + + For authorized accounts, the function returns a capability + if the given path is public or private. + If the path is a storage path, the function returns `nil`. + + `T` is the type parameter that specifies how the capability can be borrowed. + The type argument is optional, i.e. it need not be provided. + +The `getCapability` function does **not** check if the target exists. +The link is latent. +The `check` function of the capability can be used to check if the target currently exists and could be borrowed, + +- + ```cadence + fun check(): Bool + ``` + + `T` is the type parameter for the reference type. + A type argument for the parameter must be provided explicitly. + + The function returns true if the capability currently targets an object + that satisfies the given type, i.e. could be borrowed using the given type. + +Finally, the capability can be borrowed to get a reference to the stored object. +This can be done using the `borrow` function of the capability: + +- + ```cadence + fun borrow(): T? + ``` + + The function returns a reference to the object targeted by the capability, + provided it can be borrowed using the given type. + + `T` is the type parameter for the reference type. + If the function is called on a typed capability, the capability's type is used when borrowing. + If the capability is untyped, a type argument must be provided explicitly in the call to `borrow`. + + The function returns `nil` when the targeted path is empty, i.e. nothing is stored under it. + When the requested type exceeds what is allowed by the capability (or any interim capabilities), + execution will abort with an error. ```cadence -access(all) -struct Capability { - /// The address of the account which the capability targets. - access(all) - let address: Address +// Declare a resource interface named `HasCount`, that has a field `count` +// +resource interface HasCount { + count: Int +} + +// Declare a resource named `Counter` that conforms to `HasCount` +// +resource Counter: HasCount { + pub var count: Int - /// The ID of the capability. - access(all) - let id: UInt64 - - /// Returns a reference to the targeted object. - /// - /// If the capability is revoked, the function returns nil. - /// - /// If the capability targets an object in account storage, - /// and and no object is stored at the target storage path, - /// the function returns nil. - /// - /// If the targeted object cannot be borrowed using the given type, - /// the function panics. - /// - access(all) - view fun borrow(): T? - - /// Returns true if the capability currently targets an object - /// that satisfies the given type, i.e. could be borrowed using the given type. - /// - access(all) - view fun check(): Bool + pub init(count: Int) { + self.count = count + } + + pub fun increment(by amount: Int) { + self.count = self.count + amount + } } + +// In this example an authorized account is available through the constant `authAccount`. + +// Create a new instance of the resource type `Counter` +// and save it in the storage of the account. +// +// The path `/storage/counter` is used to refer to the stored value. +// Its identifier `counter` was chosen freely and could be something else. +// +authAccount.save(<-create Counter(count: 42), to: /storage/counter) + +// Create a public capability that allows access to the stored counter object +// as the type `{HasCount}`, i.e. only the functionality of reading the field +// +authAccount.link<&{HasCount}>(/public/hasCount, target: /storage/counter) ``` + +To get the published portion of an account, the `getAccount` function can be used. + +Imagine that the next example is from a different account as before. + +```cadence + +// Get the public account for the address that stores the counter +// +let publicAccount = getAccount(0x1) + +// Get a capability for the counter that is made publicly accessible +// through the path `/public/hasCount`. +// +// Use the type `&{HasCount}`, a reference to some object that provides the functionality +// of interface `HasCount`. This is the type that the capability can be borrowed as +// (it was specified in the call to `link` above). +// See the example below for borrowing using the type `&Counter`. +// +// After the call, the declared constant `countCap` has type `Capability<&{HasCount}>`, +// a capability that results in a reference that has type `&{HasCount}` when borrowed. +// +let countCap = publicAccount.getCapability<&{HasCount}>(/public/hasCount) + +// Borrow the capability to get a reference to the stored counter. +// +// This borrow succeeds, i.e. the result is not `nil`, +// it is a valid reference, because: +// +// 1. Dereferencing the path chain results in a stored object +// (`/public/hasCount` links to `/storage/counter`, +// and there is an object stored under `/storage/counter`) +// +// 2. The stored value is a subtype of the requested type `{HasCount}` +// (the stored object has type `Counter` which conforms to interface `HasCount`) +// +let countRef = countCap.borrow()! + +countRef.count // is `42` + +// Invalid: The `increment` function is not accessible for the reference, +// because it has the type `&{HasCount}`, which does not expose an `increment` function, +// only a `count` field +// +countRef.increment(by: 5) + +// Again, attempt to get a get a capability for the counter, but use the type `&Counter`. +// +// Getting the capability succeeds, because it is latent, but borrowing fails +// (the result s `nil`), because the capability was created/linked using the type `&{HasCount}`: +// +// The resource type `Counter` implements the resource interface `HasCount`, +// so `Counter` is a subtype of `{HasCount}`, but the capability only allows +// borrowing using unauthorized references of `{HasCount}` (`&{HasCount}`) +// instead of authorized references (`auth &{HasCount}`), +// so users of the capability are not allowed to borrow using subtypes, +// and they can't escalate the type by casting the reference either. +// +// This shows how parts of the functionality of stored objects +// can be safely exposed to other code +// +let countCapNew = publicAccount.getCapability<&Counter>(/public/hasCount) +let counterRefNew = countCapNew.borrow() + +// `counterRefNew` is `nil`, the borrow failed + +// Invalid: Cannot access the counter object in storage directly, +// the `borrow` function is not available for public accounts +// +let counterRef2 = publicAccount.borrow<&Counter>(from: /storage/counter) +``` + +The address of a capability can be obtained from the `address` field of the capability: + +- + ```cadence + let address: Address + ``` + + The address of the capability. diff --git a/docs/language/composite-types.mdx b/docs/language/composite-types.mdx index 94533ad..f3dbe67 100644 --- a/docs/language/composite-types.mdx +++ b/docs/language/composite-types.mdx @@ -57,11 +57,11 @@ and resources are declared using the `resource` keyword. The keyword is followed by the name. ```cadence -access(all) struct SomeStruct { +pub struct SomeStruct { // ... } -access(all) resource SomeResource { +pub resource SomeResource { // ... } ``` @@ -141,9 +141,9 @@ that is to be initialized. If a composite type is to be stored, all its field types must be storable. Non-storable types are: - Functions -- [Accounts (`Account`)](./accounts/index.mdx) +- [Accounts (`AuthAccount` / `PublicAccount`)](./accounts.mdx) - [Transactions](./transactions.md) -- [References](./references.mdx): References are ephemeral. +- [References](./references.md): References are ephemeral. Consider [storing a capability and borrowing it](./capabilities.md) when needed instead. Fields can be read (if they are constant or variable) and set (if they are variable), @@ -156,14 +156,14 @@ and the name of the field. // // Both fields are initialized through the initializer. // -// The public access modifier `access(all)` is used in this example to allow +// The public access modifier `pub` is used in this example to allow // the fields to be read in outer scopes. Fields can also be declared // private so they cannot be accessed in outer scopes. // Access control will be explained in a later section. // -access(all) struct Token { - access(all) let id: Int - access(all) var balance: Int +pub struct Token { + pub let id: Int + pub var balance: Int init(id: Int, balance: Int) { self.id = id @@ -175,19 +175,19 @@ access(all) struct Token { Note that it is invalid to provide the initial value for a field in the field declaration. ```cadence -access(all) struct StructureWithConstantField { +pub struct StructureWithConstantField { // Invalid: It is invalid to provide an initial value in the field declaration. // The field must be initialized by setting the initial value in the initializer. // - access(all) let id: Int = 1 + pub let id: Int = 1 } ``` The field access syntax must be used to access fields – fields are not available as variables. ```cadence -access(all) struct Token { - access(all) let id: Int +pub struct Token { + pub let id: Int init(initialID: Int) { // Invalid: There is no variable with the name `id` available. @@ -201,8 +201,8 @@ access(all) struct Token { The initializer is **not** automatically derived from the fields, it must be explicitly declared. ```cadence -access(all) struct Token { - access(all) let id: Int +pub struct Token { + pub let id: Int // Invalid: Missing initializer initializing field `id`. } @@ -229,23 +229,6 @@ token.id = 23 Initializers do not support overloading. -Initializers can also be declared with the `view` keyword, to indicate that they do not perform any mutating operations, -and to allow them to be called from within other `view` functions. -In an initializer, writes to `self` are considered `view` (unlike within other composite functions), -as the value being constructed here is by definition local to the context calling the initializer. - -```cadence -access(all) struct Token { - access(all) let id: Int - access(all) var balance: Int - - view init(id: Int, balance: Int) { - self.id = id - self.balance = balance - } -} -``` - ## Composite Type Functions Composite types may contain functions. @@ -256,9 +239,9 @@ that the function is called on. // Declare a structure named "Rectangle", which represents a rectangle // and has variable fields for the width and height. // -access(all) struct Rectangle { - access(all) var width: Int - access(all) var height: Int +pub struct Rectangle { + pub var width: Int + pub var height: Int init(width: Int, height: Int) { self.width = width @@ -268,7 +251,7 @@ access(all) struct Rectangle { // Declare a function named "scale", which scales // the rectangle by the given factor. // - access(all) fun scale(factor: Int) { + pub fun scale(factor: Int) { self.width = self.width * factor self.height = self.height * factor } @@ -292,14 +275,14 @@ the types are only compatible if their names match. ```cadence // Declare a structure named `A` which has a function `test` -// which has type `fun(): Void`. +// which has type `((): Void)`. // struct A { fun test() {} } // Declare a structure named `B` which has a function `test` -// which has type `fun(): Void`. +// which has type `((): Void)`. // struct B { fun test() {} @@ -336,8 +319,8 @@ Accessing a field or calling a function of a structure does not copy it. ```cadence // Declare a structure named `SomeStruct`, with a variable integer field. // -access(all) struct SomeStruct { - access(all) var value: Int +pub struct SomeStruct { + pub var value: Int init(value: Int) { self.value = value @@ -386,18 +369,18 @@ of an optional composite type. ```cadence // Declare a struct with a field and method. -access(all) struct Value { - access(all) var number: Int +pub struct Value { + pub var number: Int init() { self.number = 2 } - access(all) fun set(new: Int) { + pub fun set(new: Int) { self.number = new } - access(all) fun setAndReturn(new: Int): Int { + pub fun setAndReturn(new: Int): Int { self.number = new return new } @@ -449,18 +432,18 @@ of an optional composite type. ```cadence // Declare a struct with a field and method. -access(all) struct Value { - access(all) var number: Int +pub struct Value { + pub var number: Int init() { self.number = 2 } - access(all) fun set(new: Int) { + pub fun set(new: Int) { self.number = new } - access(all) fun setAndReturn(new: Int): Int { + pub fun setAndReturn(new: Int): Int { self.number = new return new } diff --git a/docs/language/contract-updatability.md b/docs/language/contract-updatability.md index 21b293d..bb5addf 100644 --- a/docs/language/contract-updatability.md +++ b/docs/language/contract-updatability.md @@ -4,7 +4,7 @@ sidebar_position: 23 --- ## Introduction -A [contract](./contracts.mdx) is a collection of data (its state) and +A [contract](./contracts.mdx) in Cadence is a collection of data (its state) and code (its functions) that lives in the contract storage area of an account. When a contract is updated, it is important to make sure that the changes introduced do not lead to runtime inconsistencies for already stored data. @@ -74,16 +74,16 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - access(all) contract Foo { - access(all) var a: String - access(all) var b: Int + pub contract Foo { + pub var a: String + pub var b: Int } // Updated contract - access(all) contract Foo { - access(all) var a: String + pub contract Foo { + pub var a: String } ``` - It leaves data for the removed field unused at the storage, as it is no longer accessible. @@ -93,17 +93,17 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - access(all) contract Foo { - access(all) var a: String - access(all) var b: Int + pub contract Foo { + pub var a: String + pub var b: Int } // Updated contract - access(all) contract Foo { - access(all) var b: Int - access(all) var a: String + pub contract Foo { + pub var b: Int + pub var a: String } ``` @@ -111,15 +111,15 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - access(all) contract Foo { - access(all) var a: String + pub contract Foo { + pub var a: String } // Updated contract - access(all) contract Foo { - access(self) var a: String // access modifier changed to 'access(self)' + pub contract Foo { + priv var a: String // access modifier changed to 'priv' } ``` @@ -128,16 +128,16 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - access(all) contract Foo { - access(all) var a: String + pub contract Foo { + pub var a: String } // Updated contract - access(all) contract Foo { - access(all) var a: String - access(all) var b: Int // Invalid new field + pub contract Foo { + pub var a: String + pub var b: Int // Invalid new field } ``` - Initializer of a contract only run once, when the contract is deployed for the first time. It does not rerun @@ -150,15 +150,15 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - access(all) contract Foo { - access(all) var a: String + pub contract Foo { + pub var a: String } // Updated contract - access(all) contract Foo { - access(all) var a: Int // Invalid type change + pub contract Foo { + pub var a: Int // Invalid type change } ``` - In an already stored contract, the field `a` would have a value of type `String`. @@ -180,13 +180,13 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing struct - access(all) struct Foo { + pub struct Foo { } // Upated struct - access(all) struct Foo: T { + pub struct Foo: T { } ``` - However, if adding a conformance also requires changing the existing structure (e.g: adding a new field that is @@ -203,26 +203,26 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing struct - access(all) struct Foo { + pub struct Foo { } // Changed to a struct interface - access(all) struct interface Foo { // Invalid type declaration change + pub struct interface Foo { // Invalid type declaration change } ``` - Removing an interface conformance of a struct/resource is not valid. ```cadence // Existing struct - access(all) struct Foo: T { + pub struct Foo: T { } // Upated struct - access(all) struct Foo { + pub struct Foo { } ``` @@ -253,17 +253,17 @@ Below sections describes the restrictions imposed on updating the members of a s ```cadence // Existing enum with `Int` raw type - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum with `UInt8` raw type - access(all) enum Color: UInt8 { // Invalid change of raw type - access(all) case RED - access(all) case BLUE + pub enum Color: UInt8 { // Invalid change of raw type + pub case RED + pub case BLUE } ``` - When the enum value is stored, the raw value associated with the enum-case gets stored. @@ -282,18 +282,18 @@ it originally was (type confusion). ```cadence // Existing enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE - access(all) case GREEN // valid new enum-case at the bottom + pub enum Color: Int { + pub case RED + pub case BLUE + pub case GREEN // valid new enum-case at the bottom } ``` #### Invalid Changes @@ -301,35 +301,35 @@ it originally was (type confusion). ```cadence // Existing enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum - access(all) enum Color: Int { - access(all) case RED - access(all) case GREEN // invalid new enum-case in the middle - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case GREEN // invalid new enum-case in the middle + pub case BLUE } ``` - Changing the name of an enum-case is invalid. ```cadence // Existing enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum - access(all) enum Color: Int { - access(all) case RED - access(all) case GREEN // invalid change of names + pub enum Color: Int { + pub case RED + pub case GREEN // invalid change of names } ``` - Previously stored raw values for `Color.BLUE` now represents `Color.GREEN`. i.e: The stored values have changed @@ -342,16 +342,16 @@ it originally was (type confusion). ```cadence // Existing enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum - access(all) enum Color: Int { - access(all) case RED + pub enum Color: Int { + pub case RED // invalid removal of `case BLUE` } @@ -360,17 +360,17 @@ it originally was (type confusion). ```cadence // Existing enum - access(all) enum Color: Int { - access(all) case RED - access(all) case BLUE + pub enum Color: Int { + pub case RED + pub case BLUE } // Updated enum - access(all) enum Color: UInt8 { - access(all) case BLUE // invalid change of order - access(all) case RED + pub enum Color: UInt8 { + pub case BLUE // invalid change of order + pub case RED } ``` - Raw value of an enum is implicit, and corresponds to the defined order. diff --git a/docs/language/contracts.mdx b/docs/language/contracts.mdx index e747347..5c8beed 100644 --- a/docs/language/contracts.mdx +++ b/docs/language/contracts.mdx @@ -3,18 +3,18 @@ title: Contracts sidebar_position: 22 --- -A contract is a collection of type definitions, -data (its state), and code (its functions), -that is stored in the contract storage area of an account. +A contract in Cadence is a collection of type definitions +of interfaces, structs, resources, data (its state), and code (its functions) +that lives in the contract storage area of an account in Flow. -Contracts are where all composite types interfaces for these types have to be defined. +Contracts are where all composite types like structs, resources, +events, and interfaces for these types in Cadence have to be defined. Therefore, an object of one of these types cannot exist without having been defined in a deployed Cadence contract. -Contracts can be deployed to accounts, updated, and removed from accounts -using the `contracts` object of [authorized accounts](./accounts/index.mdx). -See the [account contracts page](./accounts/contracts.mdx) -for more information about these operations. +Contracts can be created, updated, and removed using the `contracts` +object of [authorized accounts](./accounts.mdx). +This functionality is covered in the [next section](#deploying-updating-and-removing-contracts) Contracts are types. They are similar to composite types, but are stored differently than @@ -24,12 +24,11 @@ like resources or structs. Contracts stay in an account's contract storage area and can only be added, updated, or removed by the account owner with special commands. -Contracts are declared using the `contract` keyword. -The keyword is followed by the name of the contract. +Contracts are declared using the `contract` keyword. The keyword is followed +by the name of the contract. ```cadence -access(all) -contract SomeContract { +pub contract SomeContract { // ... } ``` @@ -37,35 +36,30 @@ contract SomeContract { Contracts cannot be nested in each other. ```cadence -access(all) -contract Invalid { +pub contract Invalid { // Invalid: Contracts cannot be nested in any other type. // - access(all) - contract Nested { + pub contract Nested { // ... } } ``` One of the simplest forms of a contract would just be one with a state field, -a function, and an initializer that initializes the field: +a function, and an `init` function that initializes the field: ```cadence -access(all) -contract HelloWorld { +pub contract HelloWorld { // Declare a stored state field in HelloWorld // - access(all) - let greeting: String + pub let greeting: String // Declare a function that can be called by anyone // who imports the contract // - access(all) - fun hello(): String { + pub fun hello(): String { return self.greeting } @@ -75,8 +69,10 @@ contract HelloWorld { } ``` -Transactions and other contracts can interact with contracts -by importing them at the beginning of a transaction or contract definition. +This contract could be deployed to an account and live permanently +in the contract storage. Transactions and other contracts +can interact with contracts by importing them at the beginning +of a transaction or contract definition. Anyone could call the above contract's `hello` function by importing the contract from the account it was deployed to and using the imported @@ -114,9 +110,9 @@ but they have to be in the contract and not another top-level definition. // if they were deployed to the account contract storage and // the deployment would be rejected. // -access(all) resource Vault {} -access(all) struct Hat {} -access(all) fun helloWorld(): String {} +pub resource Vault {} +pub struct Hat {} +pub fun helloWorld(): String {} let num: Int ``` @@ -132,13 +128,13 @@ there is no way to create this resource, so it would not be usable. ```cadence // Valid -access(all) contract FungibleToken { +pub contract FungibleToken { - access(all) resource interface Receiver { + pub resource interface Receiver { - access(all) balance: Int + pub balance: Int - access(all) fun deposit(from: @{Receiver}) { + pub fun deposit(from: @{Receiver}) { pre { from.balance > 0: "Deposit balance needs to be positive!" @@ -150,10 +146,10 @@ access(all) contract FungibleToken { } } - access(all) resource Vault: Receiver { + pub resource Vault: Receiver { // keeps track of the total balance of the accounts tokens - access(all) var balance: Int + pub var balance: Int init(balance: Int) { self.balance = balance @@ -161,7 +157,7 @@ access(all) contract FungibleToken { // withdraw subtracts amount from the vaults balance and // returns a vault object with the subtracted balance - access(all) fun withdraw(amount: Int): @Vault { + pub fun withdraw(amount: Int): @Vault { self.balance = self.balance - amount return <-create Vault(balance: amount) } @@ -169,7 +165,7 @@ access(all) contract FungibleToken { // deposit takes a vault object as a parameter and adds // its balance to the balance of the Account's vault, then // destroys the sent vault because its balance has been consumed - access(all) fun deposit(from: @{Receiver}) { + pub fun deposit(from: @{Receiver}) { self.balance = self.balance + from.balance destroy from } @@ -190,61 +186,303 @@ import FungibleToken from 0x42 let newVault <- create FungibleToken.Vault(balance: 10) ``` +The contract would have to either define a function that creates new +`Vault` instances or use its `init` function to create an instance and +store it in the owner's account storage. + +This brings up another key feature of contracts in Cadence. Contracts +can interact with its account's `storage` and `published` objects to store +resources, structs, and references. +They do so by using the special `self.account` object that is only accessible within the contract. + +Imagine that these were declared in the above `FungibleToken` contract. + +```cadence + + pub fun createVault(initialBalance: Int): @Vault { + return <-create Vault(balance: initialBalance) + } + + init(balance: Int) { + let vault <- create Vault(balance: 1000) + self.account.save(<-vault, to: /storage/initialVault) + } +``` + +Now, any account could call the `createVault` function declared in the contract +to create a `Vault` object. +Or the owner could call the `withdraw` function on their own `Vault` to send new vaults to others. + +```cadence +import FungibleToken from 0x42 + +// Valid: Create an instance of the `Vault` type by calling the contract's +// `createVault` function. +// +let newVault <- create FungibleToken.createVault(initialBalance: 10) +``` + ## Account access -Contracts can access the account they are deployed to: -contracts have the implicit field named `account` -which is only accessible within the contract. +Contracts have the implicit field `let account: AuthAccount`, +which is the account in which the contract is deployed too. +This gives the contract the ability to e.g. read and write to the account's storage. + +## Deploying, Updating, and Removing Contracts + +In order for a contract to be used in Cadence, it needs to be deployed to an account. +The deployed contracts of an account can be accessed through the `contracts` object. + +### Deployed Contracts + +Accounts store "deployed contracts", that is, the code of the contract: ```cadence -let account: auth(Storage, Keys, Contracts, Inbox, Capabilities) &Account`, +pub struct DeployedContract { + /// The address of the account where the contract is deployed at. + pub let address: Address + + /// The name of the contract. + pub let name: String + + /// The code of the contract. + pub let code: [UInt8] + + /// Returns an array of `Type` objects representing all the public type declarations in this contract + /// (e.g. structs, resources, enums). + /// + /// For example, given a contract + /// ``` + /// contract Foo { + /// pub struct Bar {...} + /// pub resource Qux {...} + /// } + /// ``` + /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` + pub fun publicTypes(): [Type] +} ``` -The account reference is fully entitled, -so grants access to the account's storage, keys, contracts, etc. +Note that this is not the contract instance that can be acquired by importing it. + +### Deploying a New Contract + +A new contract can be deployed to an account using the `add` function: + + ```cadence + fun add( + name: String, + code: [UInt8], + ... contractInitializerArguments + ): DeployedContract + ``` -For example, this gives the contract the ability to write to the account's storage -when the contract is initialized. + Adds the given contract to the account. + + The `code` parameter is the UTF-8 encoded representation of the source code. + The code must contain exactly one contract or contract interface, + which must have the same name as the `name` parameter. + + All additional arguments that are given are passed further to the initializer + of the contract that is being deployed. + + Fails if a contract/contract interface with the given name already exists in the account, + if the given code does not declare exactly one contract or contract interface, + or if the given name does not match the name of the contract/contract interface declaration in the code. + + Returns the [deployed contract](#deployed-contracts). + +For example, assuming the following contract code should be deployed: ```cadence -init(balance: Int) { - self.account.storage.save( - <-create Vault(balance: 1000), - to: /storage/initialVault - ) +pub contract Test { + pub let message: String + + init(message: String) { + self.message = message + } } ``` -## Contract interfaces +The contract can be deployed as follows: + +```cadence +// Decode the hex-encoded source code into a byte array +// using the built-in function `decodeHex`. +// +// (The ellipsis ... indicates the remainder of the string) +// +let code = "70756220636f6e...".decodeHex() + +// `code` has type `[UInt8]` + +let signer: AuthAccount = ... +signer.contracts.add( + name: "Test", + code: code, + message: "I'm a new contract in an existing account" +) +``` + +### Updating a Deployed Contract + + + +🚧 Status: Updating contracts is **experimental**. + +Updating contracts is currently limited to maintain data consistency. +[Certain restrictions are imposed](./contract-updatability.md). + + + +A deployed contract can be updated using the `update__experimental` function: + + ```cadence + fun update__experimental(name: String, code: [UInt8]): DeployedContract + ``` + + Updates the code for the contract/contract interface in the account. + + The `code` parameter is the UTF-8 encoded representation of the source code. + The code must contain exactly one contract or contract interface, + which must have the same name as the `name` parameter. + + Does **not** run the initializer of the contract/contract interface again. + The contract instance in the world state stays as is. + + Fails if no contract/contract interface with the given name exists in the account, + if the given code does not declare exactly one contract or contract interface, + or if the given name does not match the name of the contract/contract interface declaration in the code. + + Returns the [deployed contract](#deployed-contracts) for the updated contract. + +For example, assuming that a contract named `Test` is already deployed to the account +and it should be updated with the following contract code: + +```cadence +pub contract Test { + pub let message: String + + init(message: String) { + self.message = message + } +} +``` + +The contract can be updated as follows: + +```cadence +// Decode the hex-encoded source code into a byte array +// using the built-in function `decodeHex`. +// +// (The ellipsis ... indicates the remainder of the string) +// +let code = "70756220636f6e...".decodeHex() + +// `code` has type `[UInt8]` + +let signer: AuthAccount = ... +signer.contracts.update__experimental(name: "Test", code: code) +``` + +Updating a contract does **not** currently change any existing stored data. +Only the code of the contract is updated. + +### Getting a Deployed Contract + +A deployed contract can be gotten from an account using the `get` function: + + ```cadence + fun get(name: String): DeployedContract? + ``` + + Returns the [deployed contract](#deployed-contracts) for the contract/contract interface with the given name in the account, if any. + + Returns `nil` if no contract/contract interface with the given name exists in the account. + +For example, assuming that a contract named `Test` is deployed to an account, the contract can be retrieved as follows: + +```cadence +let signer: AuthAccount = ... +let contract = signer.contracts.get(name: "Test") +``` + +### Borrowing a Deployed Contract + +In contrast to a static contract import `import T from 0x1`, +which will always perform an import of a type, +contracts can be "borrowed" to effectively perform a dynamic import dependent on a specific execution path. + +A reference to a deployed contract contract can obtained using the `borrow` function: + + ```cadence + fun borrow(name: String): T? + ``` + + This returns a reference to the contract value stored with that name on the account, + if it exists, and if it has the provided type `T`. + + Returns `nil` if no contract/contract interface with the given name exists in the account. + +For example, assuming that a contract named `Test` which conforms to the `TestInterface` interface is deployed to an account, the contract can be retrieved as follows: + +```cadence +let signer: AuthAccount = ... +let contract: &TestInterface = signer.contracts.borrow<&TestInterface>(name: "Test") + +### Removing a Deployed Contract + +A deployed contract can be removed from an account using the `remove` function: + +```cadence +fun remove(name: String): DeployedContract? +``` + + Removes the contract/contract interface from the account which has the given name, if any. + + Returns the removed [deployed contract](#deployed-contracts), if any. + + Returns `nil` if no contract/contract interface with the given name exist in the account. + +For example, assuming that a contract named `Test` is deployed to an account, the contract can be removed as follows: + +```cadence +let signer: AuthAccount = ... +let contract = signer.contracts.remove(name: "Test") +``` + +## Contract Interfaces Like composite types, contracts can have interfaces that specify rules about their behavior, their types, and the behavior of their types. -Contract interfaces have to be declared globally. -Declarations cannot be nested in other types. +Contract interfaces have to be declared globally. Declarations +cannot be nested in other types. + +If a contract interface declares a concrete type, implementations of it +must also declare the same concrete type conforming to the type requirement. -Contract interfaces may not declare concrete types (other than events), but they can declare interfaces. If a contract interface declares an interface type, the implementing contract -does not have to also define that interface. -They can refer to that nested interface by saying `{ContractInterfaceName}.{NestedInterfaceName}` +does not have to also define that interface. They can refer to that nested +interface by saying `{ContractInterfaceName}.{NestedInterfaceName}` ```cadence // Declare a contract interface that declares an interface and a resource // that needs to implement that interface in the contract implementation. // -access(all) contract interface InterfaceExample { +pub contract interface InterfaceExample { // Implementations do not need to declare this // They refer to it as InterfaceExample.NestedInterface // - access(all) resource interface NestedInterface {} + pub resource interface NestedInterface {} // Implementations must declare this type // - access(all) resource Composite: NestedInterface {} + pub resource Composite: NestedInterface {} } -access(all) contract ExampleContract: InterfaceExample { +pub contract ExampleContract: InterfaceExample { // The contract doesn't need to redeclare the `NestedInterface` interface // because it is already declared in the contract interface @@ -252,7 +490,7 @@ access(all) contract ExampleContract: InterfaceExample { // The resource has to refer to the resource interface using the name // of the contract interface to access it // - access(all) resource Composite: InterfaceExample.NestedInterface { + pub resource Composite: InterfaceExample.NestedInterface { } } ``` diff --git a/docs/language/core-events.md b/docs/language/core-events.md index 2caff35..1115b83 100644 --- a/docs/language/core-events.md +++ b/docs/language/core-events.md @@ -1,12 +1,12 @@ --- title: Core Events -sidebar_position: 26 +sidebar_position: 25 --- Core events are events emitted directly from the FVM (Flow Virtual Machine). The events have the same name on all networks and do not follow the standard naming (they have no address). -Refer to the [public key section](./crypto.mdx#public-keys) for more details on the information provided for account key events. +Refer to the [`PublicKey` section](./crypto.mdx#publickey) for more details on the information provided for account key events. ### Account Created @@ -14,8 +14,9 @@ Event that is emitted when a new account gets created. Event name: `flow.AccountCreated` + ```cadence -access(all) event AccountCreated(address: Address) +pub event AccountCreated(address: Address) ``` | Field | Type | Description | @@ -30,7 +31,7 @@ Event that is emitted when a key gets added to an account. Event name: `flow.AccountKeyAdded` ```cadence -access(all) event AccountKeyAdded( +pub event AccountKeyAdded( address: Address, publicKey: PublicKey ) @@ -49,7 +50,7 @@ Event that is emitted when a key gets removed from an account. Event name: `flow.AccountKeyRemoved` ```cadence -access(all) event AccountKeyRemoved( +pub event AccountKeyRemoved( address: Address, publicKey: PublicKey ) @@ -68,7 +69,7 @@ Event that is emitted when a contract gets deployed to an account. Event name: `flow.AccountContractAdded` ```cadence -access(all) event AccountContractAdded( +pub event AccountContractAdded( address: Address, codeHash: [UInt8], contract: String @@ -88,7 +89,7 @@ Event that is emitted when a contract gets updated on an account. Event name: `flow.AccountContractUpdated` ```cadence -access(all) event AccountContractUpdated( +pub event AccountContractUpdated( address: Address, codeHash: [UInt8], contract: String @@ -109,7 +110,7 @@ Event that is emitted when a contract gets removed from an account. Event name: `flow.AccountContractRemoved` ```cadence -access(all) event AccountContractRemoved( +pub event AccountContractRemoved( address: Address, codeHash: [UInt8], contract: String @@ -129,7 +130,7 @@ Event that is emitted when a Capability is published from an account. Event name: `flow.InboxValuePublished` ```cadence -access(all) event InboxValuePublished(provider: Address, recipient: Address, name: String, type: Type) +pub event InboxValuePublished(provider: Address, recipient: Address, name: String, type: Type) ``` | Field | Type | Description | @@ -150,7 +151,7 @@ Event that is emitted when a Capability is unpublished from an account. Event name: `flow.InboxValueUnpublished` ```cadence -access(all) event InboxValueUnpublished(provider: Address, name: String) +pub event InboxValueUnpublished(provider: Address, name: String) ``` | Field | Type | Description | @@ -169,7 +170,7 @@ Event that is emitted when a Capability is claimed by an account. Event name: `flow.InboxValueClaimed` ```cadence -access(all) event InboxValueClaimed(provider: Address, recipient: Address, name: String) +pub event InboxValueClaimed(provider: Address, recipient: Address, name: String) ``` | Field | Type | Description | diff --git a/docs/language/crypto.mdx b/docs/language/crypto.mdx index 3fad2ae..6889747 100644 --- a/docs/language/crypto.mdx +++ b/docs/language/crypto.mdx @@ -3,24 +3,24 @@ title: Crypto sidebar_position: 30 --- -## Hash algorithms +## Hashing -The built-in [enumeration](./enumerations.md) `HashAlgorithm` -provides the set of supported hashing algorithms. +The built-in enum `HashAlgorithm` provides the set of hashing algorithms that +are supported by the language natively. ```cadence -access(all) enum HashAlgorithm: UInt8 { +pub enum HashAlgorithm: UInt8 { /// SHA2_256 is SHA-2 with a 256-bit digest (also referred to as SHA256). - access(all) case SHA2_256 = 1 + pub case SHA2_256 = 1 /// SHA2_384 is SHA-2 with a 384-bit digest (also referred to as SHA384). - access(all) case SHA2_384 = 2 + pub case SHA2_384 = 2 /// SHA3_256 is SHA-3 with a 256-bit digest. - access(all) case SHA3_256 = 3 + pub case SHA3_256 = 3 /// SHA3_384 is SHA-3 with a 384-bit digest. - access(all) case SHA3_384 = 4 + pub case SHA3_384 = 4 /// KMAC128_BLS_BLS12_381 is an instance of KECCAK Message Authentication Code (KMAC128) mac algorithm. /// Although this is a MAC algorithm, KMAC is included in this list as it can be used hash @@ -29,17 +29,17 @@ access(all) enum HashAlgorithm: UInt8 { /// It is a customized version of KMAC128 that is compatible with the hashing to curve /// used in BLS signatures. /// It is the same hasher used by signatures in the internal Flow protocol. - access(all) case KMAC128_BLS_BLS12_381 = 5 + pub case KMAC128_BLS_BLS12_381 = 5 /// KECCAK_256 is the legacy Keccak algorithm with a 256-bits digest, as per the original submission to the NIST SHA3 competition. /// KECCAK_256 is different than SHA3 and is used by Ethereum. - access(all) case KECCAK_256 = 6 + pub case KECCAK_256 = 6 /// Returns the hash of the given data - access(all) fun hash(_ data: [UInt8]): [UInt8] + pub fun hash(_ data: [UInt8]): [UInt8] /// Returns the hash of the given data and tag - access(all) fun hashWithTag(_ data: [UInt8], tag: string): [UInt8] + pub fun hashWithTag(_ data: [UInt8], tag: string): [UInt8] } ``` @@ -89,43 +89,38 @@ To define the MAC instance, `KMAC128(customizer, key, data, length)` is instanci - `data` is the input data to hash. - `length` is 1024 bytes. -## Signature algorithms +## Signing Algorithms -The built-in [enumeration](./enumerations.md) `SignatureAlgorithm` -provides the set of supported signature algorithms. +The built-in enum `SignatureAlgorithm` provides the set of signing algorithms that +are supported by the language natively. ```cadence -access(all) enum SignatureAlgorithm: UInt8 { +pub enum SignatureAlgorithm: UInt8 { /// ECDSA_P256 is ECDSA on the NIST P-256 curve. - access(all) case ECDSA_P256 = 1 + pub case ECDSA_P256 = 1 /// ECDSA_secp256k1 is ECDSA on the secp256k1 curve. - access(all) case ECDSA_secp256k1 = 2 + pub case ECDSA_secp256k1 = 2 /// BLS_BLS12_381 is BLS signature scheme on the BLS12-381 curve. /// The scheme is set-up so that signatures are in G_1 (subgroup of the curve over the prime field) /// while public keys are in G_2 (subgroup of the curve over the prime field extension). - access(all) case BLS_BLS12_381 = 3 + pub case BLS_BLS12_381 = 3 } ``` -## Public keys +## PublicKey `PublicKey` is a built-in structure that represents a cryptographic public key of a signature scheme. ```cadence -access(all) struct PublicKey { - access(all) let publicKey: [UInt8] - - access(all) let signatureAlgorithm: SignatureAlgorithm /// Verifies a signature under the given tag, data and public key. /// It uses the given hash algorithm to hash the tag and data. - access(all) - view fun verify( + pub fun verify( signature: [UInt8], signedData: [UInt8], domainSeparationTag: String, @@ -136,8 +131,7 @@ struct PublicKey { /// This function is only implemented if the signature algorithm /// of the public key is BLS (BLS_BLS12_381). /// If called with any other signature algorithm, the program aborts - access(all) - view fun verifyPoP(_ proof: [UInt8]): Bool + pub fun verifyPoP(_ proof: [UInt8]): Bool } ``` @@ -277,7 +271,7 @@ available to protect BLS verification. Cadence provides the proof of possession The proof of possession of private key is a BLS signature over the public key itself. The PoP signature follows the same requirements of a BLS signature (detailed in [Signature verification](#Signature-verification)), except it uses a special domain separation tag. The key expected to be used in KMAC128 is the UTF-8 encoding of `"BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"`. -The expected message to be signed by the PoP is the serialization of the BLS public key corresponding to the signing private key ([serialization details](#public-keys)). +The expected message to be signed by the PoP is the serialization of the BLS public key corresponding to the signing private key ([serialization details](#PublicKey)). The PoP can only be verified using the `PublicKey` method `verifyPoP`. ### BLS signature aggregation @@ -334,7 +328,7 @@ For example, to verify two signatures with equal weights for some signed data: ```cadence import Crypto -access(all) fun test main() { +pub fun test main() { let keyList = Crypto.KeyList() let publicKeyA = PublicKey( @@ -386,12 +380,12 @@ access(all) fun test main() { The API of the Crypto contract related to key lists is: ```cadence -access(all) struct KeyListEntry { - access(all) let keyIndex: Int - access(all) let publicKey: PublicKey - access(all) let hashAlgorithm: HashAlgorithm - access(all) let weight: UFix64 - access(all) let isRevoked: Bool +pub struct KeyListEntry { + pub let keyIndex: Int + pub let publicKey: PublicKey + pub let hashAlgorithm: HashAlgorithm + pub let weight: UFix64 + pub let isRevoked: Bool init( keyIndex: Int, @@ -402,12 +396,12 @@ access(all) struct KeyListEntry { ) } -access(all) struct KeyList { +pub struct KeyList { init() /// Adds a new key with the given weight - access(all) fun add( + pub fun add( _ publicKey: PublicKey, hashAlgorithm: HashAlgorithm, weight: UFix64 @@ -415,22 +409,22 @@ access(all) struct KeyList { /// Returns the key at the given index, if it exists. /// Revoked keys are always returned, but they have `isRevoked` field set to true - access(all) fun get(keyIndex: Int): KeyListEntry? + pub fun get(keyIndex: Int): KeyListEntry? /// Marks the key at the given index revoked, but does not delete it - access(all) fun revoke(keyIndex: Int) + pub fun revoke(keyIndex: Int) /// Returns true if the given signatures are valid for the given signed data - access(all) fun verify( + pub fun verify( signatureSet: [KeyListSignature], signedData: [UInt8] ): Bool } -access(all) struct KeyListSignature { - access(all) let keyIndex: Int - access(all) let signature: [UInt8] +pub struct KeyListSignature { + pub let keyIndex: Int + pub let signature: [UInt8] - access(all) init(keyIndex: Int, signature: [UInt8]) + pub init(keyIndex: Int, signature: [UInt8]) } ``` diff --git a/docs/language/enumerations.md b/docs/language/enumerations.md index 137094c..c877a55 100644 --- a/docs/language/enumerations.md +++ b/docs/language/enumerations.md @@ -1,6 +1,6 @@ --- title: Enumerations -sidebar_position: 16 +sidebar_position: 15 --- Enumerations are sets of symbolic names bound to unique, constant values, @@ -34,10 +34,10 @@ Enum cases can be compared using the equality operators `==` and `!=`. // Declare an enum named `Color` which has the raw value type `UInt8`, // and declare three enum cases: `red`, `green`, and `blue` // -access(all) enum Color: UInt8 { - access(all) case red - access(all) case green - access(all) case blue +pub enum Color: UInt8 { + pub case red + pub case green + pub case blue } // Declare a variable which has the enum type `Color` and initialize // it to the enum case `blue` of the enum diff --git a/docs/language/environment-information.md b/docs/language/environment-information.md index a284754..2f9f4d7 100644 --- a/docs/language/environment-information.md +++ b/docs/language/environment-information.md @@ -34,26 +34,26 @@ To get information about a block, the functions `getCurrentBlock` and `getBlock` The `Block` type contains the identifier, height, and timestamp: ```cadence -access(all) struct Block { +pub struct Block { /// The ID of the block. /// /// It is essentially the hash of the block. /// - access(all) let id: [UInt8; 32] + pub let id: [UInt8; 32] /// The height of the block. /// /// If the blockchain is viewed as a tree with the genesis block at the root, // the height of a node is the number of edges between the node and the genesis block /// - access(all) let height: UInt64 + pub let height: UInt64 /// The view of the block. /// /// It is a detail of the consensus algorithm. It is a monotonically increasing integer /// and counts rounds in the consensus algorithm. It is reset to zero at each spork. /// - access(all) let view: UInt64 + pub let view: UInt64 /// The timestamp of the block. /// @@ -62,7 +62,7 @@ access(all) struct Block { /// NOTE: It is included by the proposer, there are no guarantees on how much the time stamp can deviate from the true time the block was published. /// Consider observing blocks’ status changes off-chain yourself to get a more reliable value. /// - access(all) let timestamp: UFix64 + pub let timestamp: UFix64 } ``` diff --git a/docs/language/events.md b/docs/language/events.md index 40b50a5..376d5c8 100644 --- a/docs/language/events.md +++ b/docs/language/events.md @@ -1,6 +1,6 @@ --- title: Events -sidebar_position: 25 +sidebar_position: 24 --- Events are special values that can be emitted during the execution of a program. @@ -28,7 +28,7 @@ Events cannot be declared globally or within resource or struct types. // event GlobalEvent(field: Int) -access(all) contract Events { +pub contract Events { // Event with explicit argument labels // event BarEvent(labelA fieldA: Int, labelB fieldB: Int) @@ -46,7 +46,7 @@ access(all) contract Events { To emit an event from a program, use the `emit` statement: ```cadence -access(all) contract Events { +pub contract Events { event FooEvent(x: Int, y: Int) // Event with argument labels diff --git a/docs/language/functions.mdx b/docs/language/functions.mdx index 67742fe..5dd0b7b 100644 --- a/docs/language/functions.mdx +++ b/docs/language/functions.mdx @@ -216,7 +216,7 @@ except that function expressions have no name, i.e., they are anonymous. // // The function multiplies a number by two when it is called. // -// This function's type is `fun (Int): Int`. +// This function's type is `((Int): Int)`. // let double = fun (_ x: Int): Int { @@ -224,103 +224,6 @@ let double = } ``` -## View Functions - -Functions can be annotated as `view` to indicate that they do not modify any external state or any account state. -A `view` annotation can be added to the beginning of a function declaration or expression like so: - -```cadence -view access(all) fun foo(): Void {} -let x = view fun(): Void {} -access(all) struct S { - view access(all) fun foo(): Void {} - view init() -} -``` - -All functions that do not have a `view` annotation are considered non-view, -and cannot be called inside of `view` contexts, -like inside of a `view` function or in a precondition or postcondition. - -Function types can also have `view` annotations, -to be placed after the opening parenthesis but before the parameter list. -So, for example, these are valid types: - -```cadence - let f: view fun (Int): Int = ... - let h: view fun (): (view fun (): Void) = ... -``` - -Any function types without a `view` annotation will be considered non-view. - -Functions are covariant with respect to `view` annotations, -so a `view` function is a subtype of an non-view function with the same parameters and return types. -So, the following declarations would typecheck: - -```cadence - let a: view fun (): Void = view fun() {} - let b: fun (): Void = view fun() {} - let c: fun (): Void = fun() {} - let d: fun(view fun(): Void): Void = fun (x: fun(): Void) {} // contravariance -``` - -while these would not: - - -```cadence - let x: view fun (): Void = fun() {} - let y: fun(fun(): Void): Void = fun(f: view fun(): Void) {} // contravariance -``` - -The operations that are not permitted in `view` contexts are: - -* Calling a non-view function (including any functions that modify account state or storage like `save` or `load`) -* Writing to or modifying any resources -* Writing to or modifying any references -* Indexed assignment or writes to any variables not statically knowable to have been defined in the current function's scope, -or to any resources or references - -So, for example, this code would be allowed: - - ```cadence - view fun foo(): Int { - let a: [Int] = [] - a[0] = 3 - return a.length - } - ``` - - while this would not: - - ```cadence - let a: [Int] = [] - view fun foo(): Int { - a[0] = 3 - return a.length - } - ``` - -A caveat to this is that in some cases a non-`view` function that only performs a mutation that would be allowed in a `view` context will be rejected as a limitation of the analysis. -In particular, users may encounter this with arrays or dictionaries, where a function like: - -```cadence -view fun foo(): Int { - let a: [Int] = [0] - a[0] = 1 -} -``` - -is acceptable, because `a` is local to this function, while - -```cadence -view fun foo(): Int { - let a: [Int] = [0] - a.append(1) -} -``` - -will be rejected, because `append` is not `view`. - ## Function Calls Functions can be called (invoked). Function calls @@ -354,7 +257,7 @@ followed by a colon (`:`), and end with the return type. The whole function type needs to be enclosed in parentheses. ```cadence -// Declare a function named `add`, with the function type `fun(Int, Int): Int`. +// Declare a function named `add`, with the function type `((Int, Int): Int)`. // fun add(a: Int, b: Int): Int { return a + b @@ -362,9 +265,9 @@ fun add(a: Int, b: Int): Int { ``` ```cadence -// Declare a constant named `add`, with the function type `fun(Int, Int): Int` +// Declare a constant named `add`, with the function type `((Int, Int): Int)` // -let add: fun(Int, Int): Int = +let add: ((Int, Int): Int) = fun (a: Int, b: Int): Int { return a + b } @@ -376,17 +279,17 @@ If the function has no return type, it implicitly has the return type `Void`. // Declare a constant named `doNothing`, which is a function // that takes no parameters and returns nothing. // -let doNothing: fun(): Void = +let doNothing: ((): Void) = fun () {} ``` Parentheses also control precedence. -For example, a function type `fun(Int): fun(): Int` is the type +For example, a function type `((Int): ((): Int))` is the type for a function which accepts one argument with type `Int`, and which returns another function, that takes no arguments and returns an `Int`. -The type `[fun(Int): Int; 2]` specifies an array type of two functions, +The type `[((Int): Int); 2]` specifies an array type of two functions, which accept one integer and return one integer. Argument labels are not part of the function type. @@ -398,7 +301,7 @@ cannot accept argument labels. ```cadence // Declare a function which takes one argument that has type `Int`. -// The function has type `fun(Int): Void`. +// The function has type `((Int): Void)`. // fun foo1(x: Int) {} @@ -406,17 +309,17 @@ fun foo1(x: Int) {} foo1(x: 1) // Declare another function which takes one argument that has type `Int`. -// The function also has type `fun(Int): Void`. +// The function also has type `((Int): Void)`. // fun foo2(y: Int) {} // Call function `foo2`. This requires an argument label. foo2(y: 2) -// Declare a variable which has type `fun(Int): Void` and use `foo1` +// Declare a variable which has type `((Int): Void)` and use `foo1` // as its initial value. // -var someFoo: fun(Int): Void = foo1 +var someFoo: ((Int): Void) = foo1 // Call the function assigned to variable `someFoo`. // This is valid as the function types match. @@ -448,7 +351,7 @@ and assign to the variables it refers to. // Declare a function named `makeCounter` which returns a function that // each time when called, returns the next integer, starting at 1. // -fun makeCounter(): fun(): Int { +fun makeCounter(): ((): Int) { var count = 0 return fun (): Int { // NOTE: read from and assign to the non-local variable @@ -577,10 +480,6 @@ fun incrementN() { } ``` -Both preconditions and postconditions are considered [`view` contexts](#view-functions); -any operations that are not legal in functions with `view` annotations are also not allowed in conditions. -In particular, this means that if you wish to call a function in a condition, that function must be `view`. - ## Functions are Values Functions are values ("first-class"), so they may be assigned to variables and fields @@ -591,7 +490,7 @@ or passed to functions as arguments. // Declare a function named `transform` which applies a function to each element // of an array of integers and returns a new array of the results. // -access(all) fun transform(function: fun(Int): Int, integers: [Int]): [Int] { +pub fun transform(function: ((Int): Int), integers: [Int]): [Int] { var newIntegers: [Int] = [] for integer in integers { newIntegers.append(function(integer)) @@ -599,7 +498,7 @@ access(all) fun transform(function: fun(Int): Int, integers: [Int]): [Int] { return newIntegers } -access(all) fun double(_ integer: Int): Int { +pub fun double(_ integer: Int): Int { return integer * 2 } diff --git a/docs/language/glossary.mdx b/docs/language/glossary.mdx index 92b3eae..817ccd1 100644 --- a/docs/language/glossary.mdx +++ b/docs/language/glossary.mdx @@ -14,7 +14,7 @@ The `&` (ampersand) symbol has several uses. ### Reference -If an expression starts with the `&` (ampersand) symbol, it creates a [reference](./references.mdx). +If an expression starts with the `&` (ampersand) symbol, it creates a [reference](./references.md). ```cadence let a: String = "hello" @@ -23,12 +23,12 @@ let refOfA: &String = &a as &String References may also be authorized if the `&` symbol is preceded by `auth` (otherwise the reference is unauthorized). -Authorized references have the `auth` modifier, along with the set of entitlements to which the reference is authorized, -i.e. the full syntax is `auth(E, F) &T`, whereas unauthorized references do not have a modifier. +Authorized references have the `auth` modifier, i.e. the full syntax is `auth &T`, +whereas unauthorized references do not have a modifier. ```cadence let a: String = "hello" -let refOfA: auth(X) &String = &a as auth(X) &String +let refOfA: &String = &a as auth &String ``` ### Logical Operator @@ -53,8 +53,8 @@ This emphasizes the whole type acts like a resource. ```cadence // Declare a resource named `SomeResource` -access(all) resource SomeResource { - access(all) var value: Int +pub resource SomeResource { + pub var value: Int init(value: Int) { self.value = value @@ -65,7 +65,7 @@ access(all) resource SomeResource { let a: @SomeResource <- create SomeResource(value: 0) // also in functions declarations -access(all) fun use(resource: @SomeResource) { +pub fun use(resource: @SomeResource) { destroy resource } ``` @@ -156,7 +156,7 @@ let result = 4 / 2 ### Path separator -In a [path](./accounts/paths.mdx), the forward slash separates the domain, `storage` or `public`, and the identifier. +In a [Path](./accounts.mdx#paths), the forward slash separates the domain (e.g. `storage`, `private`, `public`) and the identifier. ```cadence let storagePath = /storage/path @@ -187,7 +187,7 @@ If the variable is `nil`, the move succeeds. If it is not nil, the program aborts. ```cadence -access(all) resource R {} +pub resource R {} var a: @R? <- nil a <-! create R() diff --git a/docs/language/imports.mdx b/docs/language/imports.mdx index 287c9d1..3bc725a 100644 --- a/docs/language/imports.mdx +++ b/docs/language/imports.mdx @@ -1,6 +1,6 @@ --- title: Imports -sidebar_position: 19 +sidebar_position: 18 --- Programs can import declarations (types, functions, variables, etc.) from other programs. @@ -12,7 +12,7 @@ or it can be followed by the names of the declarations that should be imported, followed by the `from` keyword, and then followed by the location. If importing a local file, the location is a string literal, and the path to the file. -Deployment of code with file imports requires the usage for the [Flow CLI](https://developers.flow.com/tools/flow-cli/index.md). +Deployment of code with file imports requires the usage for the [Flow CLI](https://developers.flow.com/tools/flow-cli/). If importing an external type in a different account, the location is an address literal, and the address diff --git a/docs/language/interfaces.mdx b/docs/language/interfaces.mdx index efacfb6..b1704ae 100644 --- a/docs/language/interfaces.mdx +++ b/docs/language/interfaces.mdx @@ -1,6 +1,6 @@ --- title: Interfaces -sidebar_position: 15 +sidebar_position: 14 --- An interface is an abstract type that specifies the behavior of types @@ -60,7 +60,9 @@ or the field requirement may specify nothing, in which case the implementation may either be a variable or a constant field. Field requirements and function requirements must specify the required level of access. -The access must be at least be public, so the `access(all)` keyword must be provided. +The access must be at least be public, so the `pub` keyword must be provided. +Variable field requirements can be specified to also be publicly settable +by using the `pub(set)` keyword. Interfaces can be used in types. This is explained in detail in the section [Interfaces in Types](#interfaces-in-types). @@ -70,16 +72,16 @@ For now, the syntax `{I}` can be read as the type of any value that implements t // Declare a resource interface for a fungible token. // Only resources can implement this resource interface. // -access(all) resource interface FungibleToken { +pub resource interface FungibleToken { // Require the implementing type to provide a field for the balance - // that is readable in all scopes (`access(all)`). + // that is readable in all scopes (`pub`). // // Neither the `var` keyword, nor the `let` keyword is used, // so the field may be implemented as either a variable // or as a constant field. // - access(all) balance: Int + pub balance: Int // Require the implementing type to provide an initializer that // given the initial balance, must initialize the balance field. @@ -109,7 +111,7 @@ access(all) resource interface FungibleToken { // The type `{FungibleToken}` is the type of any resource // that implements the resource interface `FungibleToken`. // - access(all) fun withdraw(amount: Int): @{FungibleToken} { + pub fun withdraw(amount: Int): @{FungibleToken} { pre { amount > 0: "the amount must be positive" @@ -135,7 +137,7 @@ access(all) resource interface FungibleToken { // The parameter type `{FungibleToken}` is the type of any resource // that implements the resource interface `FungibleToken`. // - access(all) fun deposit(_ token: @{FungibleToken}) { + pub fun deposit(_ token: @{FungibleToken}) { post { self.balance == before(self.balance) + token.balance: "the amount must be added to the balance" @@ -182,7 +184,7 @@ in terms of name, parameter argument labels, parameter types, and the return typ // It has a variable field named `balance`, that can be written // by functions of the type, but outer scopes can only read it. // -access(all) resource ExampleToken: FungibleToken { +pub resource ExampleToken: FungibleToken { // Implement the required field `balance` for the `FungibleToken` interface. // The interface does not specify if the field must be variable, constant, @@ -190,7 +192,7 @@ access(all) resource ExampleToken: FungibleToken { // but limit outer scopes to only read from the field, it is declared variable, // and only has public access (non-settable). // - access(all) var balance: Int + pub var balance: Int // Implement the required initializer for the `FungibleToken` interface: // accept an initial balance and initialize the `balance` field. @@ -214,7 +216,7 @@ access(all) resource ExampleToken: FungibleToken { // NOTE: neither the precondition nor the postcondition declared // in the interface have to be repeated here in the implementation. // - access(all) fun withdraw(amount: Int): @ExampleToken { + pub fun withdraw(amount: Int): @ExampleToken { self.balance = self.balance - amount return create ExampleToken(balance: amount) } @@ -235,7 +237,7 @@ access(all) resource ExampleToken: FungibleToken { // NOTE: neither the precondition nor the postcondition declared // in the interface have to be repeated here in the implementation. // - access(all) fun deposit(_ token: @{FungibleToken}) { + pub fun deposit(_ token: @{FungibleToken}) { if let exampleToken <- token as? ExampleToken { self.balance = self.balance + exampleToken.balance destroy exampleToken @@ -285,24 +287,26 @@ token.withdraw(amount: 90) The access level for variable fields in an implementation may be less restrictive than the interface requires. For example, an interface may require a field to be -at least contract-accessible (i.e. the `access(contract)` modifier is used), +at least public (i.e. the `pub` keyword is specified), and an implementation may provide a variable field which is public, -(the `access(all)` modifier is used). +but also publicly settable (the `pub(set)` keyword is specified). ```cadence -access(all) struct interface AnInterface { - // Require the implementing type to provide a contract-readable +pub struct interface AnInterface { + // Require the implementing type to provide a publicly readable // field named `a` that has type `Int`. It may be a variable // or a constant field. // - access(contract) a: Int + pub a: Int } -access(all) struct AnImplementation: AnInterface { - // Declare a public variable field named `a` that has type `Int`. +pub struct AnImplementation: AnInterface { + // Declare a publicly settable variable field named `a` that has type `Int`. // This implementation satisfies the requirement for interface `AnInterface`: + // The field is at least publicly readable, but this implementation also + // allows the field to be written to in all scopes. // - access(all) var a: Int + pub(set) var a: Int init(a: Int) { self.a = a @@ -315,7 +319,7 @@ access(all) struct AnImplementation: AnInterface { Interfaces can be used in types: The type `{I}` is the type of all objects that implement the interface `I`. -This is called a [intersection type](./intersection-types.md): +This is called a [restricted type](./restricted-types.md): Only the functionality (members and functions) of the interface can be used when accessing a value of such a type. @@ -325,18 +329,18 @@ when accessing a value of such a type. // Require implementing types to provide a field which returns the area, // and a function which scales the shape by a given factor. // -access(all) struct interface Shape { - access(all) fun getArea(): Int - access(all) fun scale(factor: Int) +pub struct interface Shape { + pub fun getArea(): Int + pub fun scale(factor: Int) } // Declare a structure named `Square` the implements the `Shape` interface. // -access(all) struct Square: Shape { +pub struct Square: Shape { // In addition to the required fields from the interface, // the type can also declare additional fields. // - access(all) var length: Int + pub var length: Int // Provided the field `area` which is required to conform // to the interface `Shape`. @@ -344,36 +348,36 @@ access(all) struct Square: Shape { // Since `area` was not declared as a constant, variable, // field in the interface, it can be declared. // - access(all) fun getArea(): Int { + pub fun getArea(): Int { return self.length * self.length } - access(all) init(length: Int) { + pub init(length: Int) { self.length = length } // Provided the implementation of the function `scale` // which is required to conform to the interface `Shape`. // - access(all) fun scale(factor: Int) { + pub fun scale(factor: Int) { self.length = self.length * factor } } // Declare a structure named `Rectangle` that also implements the `Shape` interface. // -access(all) struct Rectangle: Shape { - access(all) var width: Int - access(all) var height: Int +pub struct Rectangle: Shape { + pub var width: Int + pub var height: Int // Provided the field `area which is required to conform // to the interface `Shape`. // - access(all) fun getArea(): Int { + pub fun getArea(): Int { return self.width * self.height } - access(all) init(width: Int, height: Int) { + pub init(width: Int, height: Int) { self.width = width self.height = height } @@ -381,7 +385,7 @@ access(all) struct Rectangle: Shape { // Provided the implementation of the function `scale` // which is required to conform to the interface `Shape`. // - access(all) fun scale(factor: Int) { + pub fun scale(factor: Int) { self.width = self.width * factor self.height = self.height * factor } @@ -465,40 +469,6 @@ struct SomeInner: OuterInterface.InnerInterface {} ``` -Contract interfaces may also declare [events](./events.md), -which also do not require implementing types of the outer interface to "implement" the event. -The event can be emitted in the declaring interface, in a condition or in a default implementation of a function. E.g. - -```cadence -// Declare a contract interface -// -contract interface ContractInterface { - // some event declaration - // - event SomeEvent() - - // some function that emits `SomeEvent` when called - // - fun eventEmittingFunction() { - pre { - emit SomeEvent() - } - } -} - -// A contract implementing `ContractInterface` -// Note that no declaration of `SomeEvent` is required -// -contract ImplementingContract: ContractInterface { - // implementation of `eventEmittingFunction`; - // this will emit `SomeEvent` when called - // - fun eventEmittingFunction() { - // ... - } -} -``` - ## Interface Default Functions Interfaces can provide default functions: @@ -540,411 +510,38 @@ Interfaces cannot provide default initializers or default destructors. Only one conformance may provide a default function. -## Interface inheritance - -An interface can inherit from (conform to) other interfaces of the same kind. -For example, a resource interface can inherit from another resource interface, but cannot inherit from a struct interface. -When an interface inherits from another, all the fields, functions, and types of the parent interface are implicitly -available to the inheriting interface. - -```cadence -access(all) resource interface Receiver { - access(all) fun deposit(_ something: @AnyResource) -} - -// `Vault` interface inherits from `Receiver` interface. -access(all) resource interface Vault: Receiver { - access(all) fun withdraw(_ amount: Int): @Vault -} -``` - -In the example above, `Vault` inherits `Receiver`. Anyone implementing the `Vault` interface would also have to -implement the `Receiver` interface. - -```cadence -access(all) resource MyVault: Vault { - // Must implement all the methods coming from both `Vault` and `Receiver` interfaces. - access(all) fun deposit(_ something: @AnyResource) {} - - access(all) fun withdraw(_ amount: Int): @Vault {} -} -``` - -### Duplicate interface members - -When an interface implements another interface, it is possible for the two interfaces to have members with the same name. -The following sections explain how these ambiguities are resolved for different scenarios. - -#### Fields - -If two fields with identical names have identical types, then it will be valid. - -```cadence -access(all) resource interface Receiver { - access(all) var id: UInt64 -} - -access(all) resource interface Vault: Receiver { - // `id` field has the same type as the `Receiver.id`. Hence this is valid. - access(all) var id: UInt64 -} -``` - -Otherwise, interface conformance is not valid. - -```cadence -access(all) resource interface Receiver { - access(all) var id: Int -} - -access(all) resource interface Vault: Receiver { - // `id` field has a different type than the `Receiver.id`. Hence this is invalid. - access(all) var id: UInt64 -} -``` - -#### Functions - -If two functions with identical names also have identical signatures, that is valid. - -```cadence -access(all) resource interface Receiver { - access(all) fun deposit(_ something: @AnyResource) -} - -access(all) resource interface Vault: Receiver { - // `deposit` function has the same signature as the `Receiver.deposit`. - // Also none of them have any default implementations. - // Hence this is valid. - access(all) fun deposit(_ something: @AnyResource) -} -``` - -If the signatures of the two functions are different, then the interface conformance is not valid. - -```cadence -access(all) resource interface Receiver { - access(all) fun deposit(_ something: @AnyResource) -} - -access(all) resource interface Vault: Receiver { - // Error: `deposit` function has a different signature compared to the `Receiver.deposit`. - // So these two cannot co-exist. - access(all) fun deposit() -} -``` - -#### Functions with conditions - -If the two functions with identical names and signatures have pre/post conditions, then it will still be valid. -However, the pre/post conditions are linearized (refer to the [linearizing conditions section](#linearizing-conditions)) -to determine the order of the execution of the conditions. -Given the pre/post conditions are `view` only, the order of execution would not have an impact on the conditions. - -```cadence -access(all) resource interface Receiver { - access(all) fun deposit(_ something: @AnyResource) { - pre{ self.balance > 100 } - } -} - -access(all) resource interface Vault: Receiver { - // `deposit` function has the same signature as the `Receiver.deposit`. - // Having pre/post condition is valid. - // Both conditions would be executed, in a pre-determined order. - access(all) fun deposit(_ something: @AnyResource) { - pre{ self.balance > 50 } - } -} -``` - -#### Default functions - -An interface can provide a default implementation to an inherited function. - -```cadence -access(all) resource interface Receiver { - access(all) fun log(_ message: String) -} - -access(all) resource interface Vault: Receiver { - // Valid: Provides the implementation for `Receiver.log` method. - access(all) fun log(_ message: String) { - log(message.append("from Vault")) - } -} -``` - -However, an interface cannot override an inherited default implementation of a function. - -```cadence -access(all) resource interface Receiver { - access(all) fun log(_ message: String) { - log(message.append("from Receiver")) - } -} - -access(all) resource interface Vault: Receiver { - // Invalid: Cannot override the `Receiver.log` method. - access(all) fun log(_ message: String) { - log(message.append("from Vault")) - } -} -``` - -It is also invalid to have two or more inherited default implementations for an interface. - -```cadence -access(all) resource interface Receiver { - access(all) fun log(_ message: String) { - log(message.append("from Receiver")) - } -} - -access(all) resource interface Provider { - access(all) fun log(_ message: String) { - log(message.append("from Provider")) - } -} - -// Invalid: Two default functions from two interfaces. -access(all) resource interface Vault: Receiver, Provider {} -``` - -Having said that, there can be situations where the same default function can be available via different -inheritance paths. - -```cadence -access(all) resource interface Logger { - access(all) fun log(_ message: String) { - log(message.append("from Logger")) - } -} - -access(all) resource interface Receiver: Logger {} - -access(all) resource interface Provider: Logger {} - -// Valid: `Logger.log()` default function is visible to the `Vault` interface -// via both `Receiver` and `Provider`. -access(all) resource interface Vault: Receiver, Provider {} -``` - -In the above example, `Logger.log()` default function is visible to the `Vault` interface via both `Receiver` and `Provider`. -Even though it is available from two different interfaces, they are both referring to the same -default implementation. -Therefore, the above code is valid. - -#### Conditions with Default functions - -A more complex situation is where a default function is available via one inheritance path and a pre/post condition -is available via another inheritance path. - -```cadence -access(all) resource interface Receiver { - access(all) fun log(_ message: String) { - log(message.append("from Receiver")) - } -} - -access(all) resource interface Provider { - access(all) fun log(_ message: String) { - pre{ message != "" } - } -} - -// Valid: Both the default function and the condition would be available. -access(all) resource interface Vault: Receiver, Provider {} -``` - -In such situations, all rules applicable for default functions inheritance as well as condition inheritance -would be applied. -Thus, the default function from coming from the `Receiver` interface, and the condition comes from the `Provider` -interface would be made available for the inherited interface. - -#### Types and event definitions - -Type and event definitions would also behave similarly to the default functions. -Inherited interfaces can override type definitions and event definitions. - -```cadence -access(all) contract interface Token { - access(all) struct Foo {} -} - -access(all) contract interface NonFungibleToken: Token { - access(all) struct Foo {} -} - -access(all) contract MyToken: NonFungibleToken { - access(all) fun test() { - let foo: Foo // This will refer to the `NonFungibleToken.Foo` - } -} -``` - -If a user needed to access the `Foo` struct coming from the super interface `Token`, then they can -access it using the fully qualified name. e.g: `let foo: Token.Foo`. - -However, it is not allowed to have two or more inherited type/events definitions with identical names for an interface. - -```cadence -access(all) contract interface Token { - access(all) struct Foo {} -} - -access(all) contract interface Collectible { - access(all) struct Foo {} -} - -// Invalid: Two type definitions with the same name from two interfaces. -access(all) contract NonFungibleToken: Token, Collectible { -} -``` -Similar to default functions, there can be situations where the same type/event definition can be available -via different inheritance paths. - -```cadence -access(all) contract interface Logger { - access(all) struct Foo {} -} - -access(all) contract interface Token: Logger {} - -access(all) contract interface Collectible: Logger {} - -// Valid: `Logger.Foo` struct is visible to the `NonFungibleToken` interface via both `Token` and `Collectible`. -access(all) contract interface NonFungibleToken: Token, Collectible {} -``` - -In the above example, `Logger.Foo` type definition is visible to the `NonFungibleToken` interface via both `Token` -and `Collectible`. -Even though it is available from two different interfaces, they are both referring to the same -type definition. -Therefore, the above code is valid. - -However, if at least one of the interfaces in the middle of the chain also overrides the type definition `Foo`, -then the code becomes invalid, as there are multiple implementations present now, which leads to ambiguity. - -```cadence -access(all) contract interface Logger { - access(all) struct Foo {} -} - -access(all) contract interface Token: Logger { - access(all) struct Foo {} -} - -access(all) contract interface Collectible: Logger {} - -// Invalid: The default implementation of the `Foo` struct by the `Logger` -// interface is visible to the `NonFungibleToken` via the `Collectible` interface. -// Another implementation of `Foo` struct is visible to the `NonFungibleToken` via the `Token` interface. -// This creates ambiguity. -access(all) resource interface NonFungibleToken: Token, Provider {} -``` - - -### Linearizing Conditions - -As mentioned in the [functions with conditions](#functions-with-conditions) section, it would be required to linearize -the function conditions, to determine the order in which pre- and post-conditions are executed. -This is done by linearizing the interfaces, and hence conditions, in a **depth-first pre-ordered manner, without duplicates**. - -For example, consider an interface inheritance hierarchy as below: -``` - A - / \ - B C - / \ / - D E -where an edge from A (top) to B (bottom) means A inherits B. -``` +## Nested Type Requirements -This would convert to a Cadence implementation similar to: - -```cadence -struct interface A: B, C { - access(all) fun test() { - pre { print("A") } - } -} - -struct interface B: D, E { - access(all) fun test() { - pre { print("B") } - } -} - -struct interface C: E { - access(all) fun test() { - pre { print("C") } - } -} + -struct interface D { - access(all) fun test() { - pre { print("D") } - } -} +🚧 Status: Currently only contracts and contract interfaces support nested type requirements. -struct interface E { - access(all) fun test() { - pre { print("E") } - } -} -``` + -Any concrete type implementing interface `A` would be equivalent to implementing all interfaces from `A` to `E`, linearized. +Interfaces can require implementing types to provide concrete nested types. +For example, a resource interface may require an implementing type to provide a resource type. ```cadence -struct Foo: A { - access(all) fun test() { - pre { print("Foo") } +// Declare a resource interface named `FungibleToken`. +// +// Require implementing types to provide a resource type named `Vault` +// which must have a field named `balance`. +// +resource interface FungibleToken { + pub resource Vault { + pub balance: Int } } -``` - -The linearized interface order would be: [A, B, D, E, C]. - -i.e: same as having: -```cadence -struct Foo: A, B, D, C, E { - access(all) fun test() { - pre { print("Foo") } +// Declare a resource named `ExampleToken` that implements the `FungibleToken` interface. +// +// The nested type `Vault` must be provided to conform to the interface. +// +resource ExampleToken: FungibleToken { + pub resource Vault { + pub var balance: Int + init(balance: Int) { + self.balance = balance + } } } ``` - -Thus, invoking `test` method of `Foo` would first invoke the pre-conditions of [A, B, D, E, C], in that particular order, -and eventually runs the pre-condition of the concrete implementation `Foo`. - -```cadence -let foo = Foo() -foo.test() -``` - -Above will print: - -``` -A -B -D -E -C -Foo -``` - -Similarly, for post-conditions, the same linearization of interfaces would be used, and the post-conditions are executed -in the reverse order. -For example, replacing the `pre` conditions in the above example with `post` conditions with the exact same content would -result in an output similar to: - -``` -Foo -C -E -D -B -A -``` diff --git a/docs/language/operators.md b/docs/language/operators.md index 3acf814..7def4bc 100644 --- a/docs/language/operators.md +++ b/docs/language/operators.md @@ -623,7 +623,6 @@ They're often used in low-level programming. For unsigned integers, the bitwise shifting operators perform [logical shifting](https://en.wikipedia.org/wiki/Logical_shift), for signed integers, they perform [arithmetic shifting](https://en.wikipedia.org/wiki/Arithmetic_shift). -Also note that for `a << b` or `a >> b`, `b` must fit into a 64-bit integer. ## Ternary Conditional Operator diff --git a/versioned_docs/version-current_0.42/0.42/language/references.md b/docs/language/references.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/references.md rename to docs/language/references.md diff --git a/docs/language/resources.mdx b/docs/language/resources.mdx index 645f28b..17e2dda 100644 --- a/docs/language/resources.mdx +++ b/docs/language/resources.mdx @@ -40,8 +40,8 @@ and when it is returned from a function. ```cadence // Declare a resource named `SomeResource`, with a variable integer field. // -access(all) resource SomeResource { - access(all) var value: Int +pub resource SomeResource { + pub var value: Int init(value: Int) { self.value = value @@ -69,7 +69,7 @@ b.value // equals 0 // // The parameter has a resource type, so the type annotation must be prefixed with `@`. // -access(all) fun use(resource: @SomeResource) { +pub fun use(resource: @SomeResource) { // ... } @@ -128,7 +128,7 @@ let someResource: @SomeResource <- create SomeResource(value: 5) // // The parameter has a resource type, so the type annotation must be prefixed with `@`. // -access(all) fun use(resource: @SomeResource) { +pub fun use(resource: @SomeResource) { destroy resource } @@ -137,7 +137,7 @@ access(all) fun use(resource: @SomeResource) { // The return type is a resource type, so the type annotation must be prefixed with `@`. // The return statement must also use the `<-` operator to make it explicit the resource is moved. // -access(all) fun get(): @SomeResource { +pub fun get(): @SomeResource { let newResource <- create SomeResource() return <-newResource } @@ -149,7 +149,7 @@ Resources **must** be used exactly once. // Declare a function which consumes a resource but does not use it. // This function is invalid, because it would cause a loss of the resource. // -access(all) fun forgetToUse(resource: @SomeResource) { +pub fun forgetToUse(resource: @SomeResource) { // Invalid: The resource parameter `resource` is not used, but must be. } ``` @@ -177,7 +177,7 @@ res.value // This function is invalid, because it does not always use the resource parameter, // which would cause a loss of the resource. // -access(all) fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) { +pub fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) { if destroyResource { destroy resource } @@ -192,7 +192,7 @@ access(all) fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) // This function is valid, as it always uses the resource parameter, // and does not cause a loss of the resource. // -access(all) fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { +pub fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { if destroyResource { destroy resource } else { @@ -208,7 +208,7 @@ access(all) fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { // This function is invalid, because it does not always use the resource parameter, // which would cause a loss of the resource. // -access(all) fun returnBeforeDestroy(move: Bool) { +pub fun returnBeforeDestroy(move: Bool) { let res <- create SomeResource(value: 1) if move { use(resource: <-res) @@ -234,7 +234,7 @@ Instead, use a swap statement (`<->`) or shift statement (`<- target <-`) to replace the resource variable with another resource. ```cadence -access(all) resource R {} +pub resource R {} var x <- create R() var y <- create R() @@ -268,7 +268,7 @@ A resource may have only one destructor. ```cadence var destructorCalled = false -access(all) resource Resource { +pub resource Resource { // Declare a destructor for the resource, which is executed // when the resource is destroyed. @@ -292,7 +292,7 @@ it **must** declare a destructor, which **must** invalidate all resource fields, i.e. move or destroy them. ```cadence -access(all) resource Child { +pub resource Child { let name: String init(name: String) @@ -304,7 +304,7 @@ access(all) resource Child { // The resource *must* declare a destructor // and the destructor *must* invalidate the resource field. // -access(all) resource Parent { +pub resource Parent { let name: String var child: @Child @@ -355,7 +355,7 @@ resource R {} // the resource parameter `resource`. Each call to the returned function // would return the resource, which should not be possible. // -fun makeCloner(resource: @R): fun(): @R { +fun makeCloner(resource: @R): ((): @R) { return fun (): @R { return <-resource } @@ -599,8 +599,8 @@ Do not rely on or assume any particular behaviour in Cadence programs. ## Resource Owner -Resources have the implicit field `let owner: &Account?`. -If the resource is currently [stored in an account](./accounts/storage.mdx), +Resources have the implicit field `let owner: PublicAccount?`. +If the resource is currently [stored in an account](./accounts.mdx#account-storage), then the field contains the publicly accessible portion of the account. Otherwise the field is `nil`. diff --git a/versioned_docs/version-current_0.42/0.42/language/restricted-types.md b/docs/language/restricted-types.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/restricted-types.md rename to docs/language/restricted-types.md diff --git a/docs/language/run-time-types.md b/docs/language/run-time-types.md index 5ede0b0..81da987 100644 --- a/docs/language/run-time-types.md +++ b/docs/language/run-time-types.md @@ -91,25 +91,26 @@ Run-time types can also be constructed from type identifier strings using built- ```cadence fun CompositeType(_ identifier: String): Type? fun InterfaceType(_ identifier: String): Type? -fun IntersectionType(types: [String]): Type? +fun RestrictedType(identifier: String?, restrictions: [String]): Type? ``` -Given a type identifier (or a list of identifiers for interfaces -in the case of `IntersectionType`), these functions will look up nominal types and +Given a type identifier (as well as a list of identifiers for restricting interfaces +in the case of `RestrictedType`), these functions will look up nominal types and produce their run-time equivalents. If the provided identifiers do not correspond -to any types, or (in the case of `IntersectionType`) the provided combination of +to any types, or (in the case of `RestrictedType`) the provided combination of identifiers would not type-check statically, these functions will produce `nil`. ```cadence -struct Test: I {} +struct Test {} struct interface I {} let type: Type = CompositeType("A.0000000000000001.Test") // `type` is `Type` -let type2: Type = IntersectionType( +let type2: Type = RestrictedType( + identifier: type.identifier, restrictions: ["A.0000000000000001.I"] ) -// `type2` is `Type<{I}>` +// `type2` is `Type` ``` Other built-in functions will construct compound types from other run-types. @@ -123,7 +124,7 @@ fun FunctionType(parameters: [Type], return: Type): Type fun DictionaryType(key: Type, value: Type): Type? // returns `nil` if `type` is not a reference type fun CapabilityType(_ type: Type): Type? -fun ReferenceType(entitlements: [String], type: Type): Type? +fun ReferenceType(authorized: bool, type: Type): Type ``` ### Asserting the Type of a Value @@ -180,21 +181,21 @@ something.isInstance(Type()) // is `false` For example, this allows implementing a marketplace sale resource: ```cadence -access(all) resource SimpleSale { +pub resource SimpleSale { /// The resource for sale. /// Once the resource is sold, the field becomes `nil`. /// - access(all) var resourceForSale: @AnyResource? + pub var resourceForSale: @AnyResource? /// The price that is wanted for the purchase of the resource. /// - access(all) let priceForResource: UFix64 + pub let priceForResource: UFix64 /// The type of currency that is required for the purchase. /// - access(all) let requiredCurrency: Type - access(all) let paymentReceiver: Capability<&{FungibleToken.Receiver}> + pub let requiredCurrency: Type + pub let paymentReceiver: Capability<&{FungibleToken.Receiver}> /// `paymentReceiver` is the capability that will be borrowed /// once a valid purchase is made. @@ -225,7 +226,7 @@ access(all) resource SimpleSale { /// If the purchase succeeds, the resource for sale is returned. /// If the purchase fails, the program aborts. /// - access(all) fun buyObject(with funds: @FungibleToken.Vault): @AnyResource { + pub fun buyObject(with funds: @FungibleToken.Vault): @AnyResource { pre { // Ensure the resource is still up for sale self.resourceForSale != nil: "The resource has already been sold" diff --git a/docs/language/transactions.md b/docs/language/transactions.md index dda8d2f..d84f179 100644 --- a/docs/language/transactions.md +++ b/docs/language/transactions.md @@ -1,63 +1,46 @@ --- title: Transactions -sidebar_position: 24 +sidebar_position: 26 --- -Transactions are objects that are signed with keys of one or more [accounts](./accounts/index.mdx) -and are sent to the chain to interact with it and perform state changes. +Transactions are objects that are signed by one or more [accounts](./accounts.mdx) +and are sent to the chain to interact with it. -Transaction can [import](./imports.mdx) any number of types from any account using the import syntax. +Transactions are structured as such: + +First, the transaction can import any number of types from external accounts +using the import syntax. ```cadence import FungibleToken from 0x01 ``` -A transaction is declared using the `transaction` keyword -and its contents are contained in curly braces. +The body is declared using the `transaction` keyword and its contents +are contained in curly braces. -The body of the transaction can declare local variables -that are valid throughout the whole of the transaction. +Next is the body of the transaction, +which first contains local variable declarations that are valid +throughout the whole of the transaction. ```cadence transaction { // transaction contents let localVar: Int - // ... -} -``` - -## Transaction parameters - -Transactions can have parameters. -Transaction parameters are declared like function parameters. -The arguments for the transaction are passed along with the transaction. - -Transaction parameters are accessible throughout the whole of the transaction. - -```cadence -// Declare a transaction which has one parameter named `amount` -// that has the type `UFix64` -// -transaction(amount: UFix64) { - + ... } ``` -## Transaction phases - -Transactions are executed in four phases: -preparation, pre-conditions, execution, and post-conditions, in that order. +Then, four optional main phases: +Preparation, preconditions, execution, and postconditions, in that order. The preparation and execution phases are blocks of code that execute sequentially. -The pre-conditions and post-condition are just like -[conditions in functions](./functions.mdx#function-preconditions-and-postconditions). -The following empty Cadence transaction has no logic, -but demonstrates the syntax for each phase, in the order these phases are executed: +The following empty Cadence transaction contains no logic, +but demonstrates the syntax for each phase, in the order these phases will be executed: ```cadence transaction { - prepare(signer1: &Account, signer2: &Account) { + prepare(signer1: AuthAccount, signer2: AuthAccount) { // ... } @@ -77,62 +60,65 @@ transaction { Although optional, each phase serves a specific purpose when executing a transaction and it is recommended that developers use these phases when creating their transactions. +The following will detail the purpose of and how to use each phase. -### Prepare phase +## Transaction Parameters -The `prepare` phase is used when the transaction needs access -to the accounts which signed (authorized) the transaction. - -Access to the signing accounts is **only possible inside the `prepare` phase**. - -For each signer of the transaction, -a [reference](./references.mdx) to the signing account is passed as an argument to the `prepare` phase. -The reference may be authorized, requesting certain [access to the account](./accounts/index.mdx#account-access). +Transactions may declare parameters. +Transaction parameters are declared like function parameters. +The arguments for the transaction are passed in the sent transaction. -For example, if the transaction has two signers, -the prepare **must** have two parameters of type `&Account`. +Transaction parameters are accessible in all phases. ```cadence -prepare(signer1: &Account, signer2: &Account) { - // ... +// Declare a transaction which has one parameter named `amount` +// that has the type `UFix64` +// +transaction(amount: UFix64) { + } ``` -For instance, to request write access to an [account's storage](./accounts/storage.mdx), -the transaction can request an authorized reference: +## Prepare phase + +The `prepare` phase is used when access to the private `AuthAccount` object +of **signing accounts** is required for your transaction. + +Direct access to signing accounts is **only possible inside the `prepare` phase**. + +For each signer of the transaction the signing account is passed as an argument to the `prepare` phase. +For example, if the transaction has three signers, +the prepare **must** have three parameters of type `AuthAccount`. ```cadence -prepare(signer: auth(Storage) &Account) { - // ... -} + prepare(signer1: AuthAccount) { + // ... + } ``` -As a best practice, only use the `prepare` phase to define and execute logic -that requires [write access](./accounts/index.mdx#performing-write-operations) to the signing accounts, +As a best practice, only use the `prepare` phase to define and execute logic that requires access +to the `AuthAccount` objects of signing accounts, and *move all other logic elsewhere*. - Modifications to accounts can have significant implications, -so keep this phase clear of unrelated logic. -This ensures that users can easily read and understand the logic of the transaction -and how it affects their account. +so keep this phase clear of unrelated logic to ensure users of your contract are able to easily read +and understand logic related to their private account objects. -The prepare phase serves a similar purpose as the -[initializer of a composite](https://developers.flow.com/next/cadence/language/composite-types#composite-type-fields). +The prepare phase serves a similar purpose as the initializer of a contract/resource/structure. For example, if a transaction performs a token transfer, put the withdrawal in the `prepare` phase, as it requires access to the account storage, but perform the deposit in the `execute` phase. -### Pre-conditions +`AuthAccount` objects have the permissions +to read from and write to the `/storage/` and `/private/` areas +of the account, which cannot be directly accessed anywhere else. +They also have the permission to create and delete capabilities that +use these areas. -Transaction pre-conditions are just like -[pre-conditions of functions](./functions.mdx#function-preconditions-and-postconditions). +## Pre Phase -Pre-conditions are optional and are declared in a `pre` block. -They are executed after the `prepare` phase, -and are used for checking if explicit conditions hold before executing the remainder of the transaction. -The block can have zero or more conditions. - -For example, a pre-condition might check the balance before transferring tokens between accounts. +The `pre` phase is executed after the `prepare` phase, and is used for checking +if explicit conditions hold before executing the remainder of the transaction. +A common example would be checking requisite balances before transferring tokens between accounts. ```cadence pre { @@ -140,91 +126,88 @@ pre { } ``` -If any of the pre-conditions fail, -then the remainder of the transaction is not executed and it is completely reverted. +If the `pre` phase throws an error, or does not return `true` the remainder of the transaction +is not executed and it will be completely reverted. -### Execute phase +## Execute Phase -The `execute` block executes the main logic of the transaction. +The `execute` phase does exactly what it says, it executes the main logic of the transaction. This phase is optional, but it is a best practice to add your main transaction logic in the section, so it is explicit. -It is impossible to access the references to the transaction's signing accounts in the `execute` phase. - ```cadence -transaction { - prepare(signer: auth(LoadValue) &Account) {} - - execute { - // Invalid: Cannot access the `signer` account reference, as it is not in scope - let resource <- signer.storage.load<@Resource>(from: /storage/resource) - destroy resource - - // Valid: Can obtain an unauthorized reference to any account - let otherAccount = getAccount(0x3) - } +execute { + // Invalid: Cannot access the authorized account object, + // as `account1` is not in scope + let resource <- account1.load<@Resource>(from: /storage/resource) + destroy resource + + // Valid: Can access any account's public Account object + let publicAccount = getAccount(0x03) } ``` -### Post-conditions +You **may not** access private `AuthAccount` objects in the `execute` phase, +but you may get an account's `PublicAccount` object, +which allows reading and calling methods on objects +that an account has published in the public domain of its account (resources, contract methods, etc.). -Transaction post-conditions are just like -[post-conditions of functions](./functions.mdx#function-preconditions-and-postconditions). +## Post Phase -Post-conditions are optional and are declared in a `post` block. -They are executed after the execution phase, -and are used to verify that the transaction logic has been executed properly. -The block can have zero or more conditions. +Statements inside of the `post` phase are used +to verify that your transaction logic has been executed properly. +It contains zero or more condition checks. -For example, a token transfer transaction can ensure that the final balance has a certain value: +For example, a transfer transaction might ensure that the final balance has a certain value, +or e.g. it was incremented by a specific amount. ```cadence post { - signer.balance == 30.0: "Balance after transaction is incorrect!" + result.balance == 30: "Balance after transaction is incorrect!" } ``` -If any of the post-conditions fail, -then the transaction fails and is completely reverted. +If any of the condition checks result in `false`, the transaction will fail and be completely reverted. -### Pre-conditions and post-conditions +Only condition checks are allowed in this section. +No actual computation or modification of values is allowed. -Another function of the pre-conditions and post-conditions -is to describe the effects of a transaction on the involved accounts. -They are essential for users to verify what a transaction does before submitting it. -The conditions an easy way to introspect transactions before they are executed. +**A Note about `pre` and `post` Phases** -For example, the software that a user uses to sign and send a transaction -could analyze and interpret the transaction into a human-readable description, like -"This transaction will transfer 30 tokens from A to B. +Another function of the `pre` and `post` phases is to help provide information +about how the effects of a transaction on the accounts and resources involved. +This is essential because users may want to verify what a transaction does before submitting it. +`pre` and `post` phases provide a way to introspect transactions before they are executed. + +For example, in the future the phases could be analyzed and interpreted to the user +in the software they are using, +e.g. "this transaction will transfer 30 tokens from A to B. The balance of A will decrease by 30 tokens and the balance of B will increase by 30 tokens." ## Summary -Transactions use phases to make the transaction's code / intent more readable. -They provide a way for developers to separate the transaction logic. -Transactions also provide a way to check the state prior / after transaction execution, -to prevent the transaction from running, or reverting changes made by the transaction if needed. +Cadence transactions use phases to make the transaction's code / intent more readable +and to provide a way for developer to separate potentially 'unsafe' account +modifying code from regular transaction logic, +as well as provide a way to check for error prior / after transaction execution, +and abort the transaction if any are found. The following is a brief summary of how to use the `prepare`, `pre`, `execute`, -and `post` blocks in a transaction to implement the transaction's phases: +and `post` phases in a Cadence transaction. ```cadence transaction { - prepare(signer1: &Account) { - // Access signing accounts of the transaction. + prepare(signer1: AuthAccount) { + // Access signing accounts for this transaction. // - // Avoid logic that does not need access to the signing accounts. + // Avoid logic that does not need access to signing accounts. // - // The signing accounts can't be accessed anywhere else in the transaction. + // Signing accounts can't be accessed anywhere else in the transaction. } pre { // Define conditions that must be true - // for the transaction to execute. - // - // Define the expected state of things - // as they should be before the transaction is executed. + // for this transaction to execute. } execute { @@ -233,14 +216,11 @@ transaction { } post { - // Define conditions that must be true - // for the transaction to be committed. - // // Define the expected state of things // as they should be after the transaction executed. // // Also used to provide information about what changes - // the transaction will make to the signing accounts. + // this transaction will make to accounts in this transaction. } } ``` diff --git a/docs/language/type-inference.md b/docs/language/type-inference.md index 8f02e9d..a076163 100644 --- a/docs/language/type-inference.md +++ b/docs/language/type-inference.md @@ -109,7 +109,7 @@ let add = (a: Int8, b: Int8): Int { return a + b } -// `add` has type `fun(Int8, Int8): Int` +// `add` has type `((Int8, Int8): Int)` ``` Type inference is performed for each expression / statement, and not across statements. diff --git a/docs/language/values-and-types.mdx b/docs/language/values-and-types.mdx index df74f86..39b4763 100644 --- a/docs/language/values-and-types.mdx +++ b/docs/language/values-and-types.mdx @@ -99,8 +99,6 @@ and can represent values in the following ranges: - **`Word16`**: 0 through 2^16 − 1 (65535) - **`Word32`**: 0 through 2^32 − 1 (4294967295) - **`Word64`**: 0 through 2^64 − 1 (18446744073709551615) -- **`Word128`**: 0 through 2^128 − 1 (340282366920938463463374607431768211455) -- **`Word256`**: 0 through 2^256 − 1 (115792089237316195423570985008687907853269984665640564039457584007913129639935) The types are independent types, i.e. not subtypes of each other. @@ -950,21 +948,6 @@ let c = str[0] // is the Character "a" let uwu: String = String.fromCharacters(rawUwU) // "UwU" ``` -- - ```cadence - let utf8: [UInt8] - ``` - - The byte array of the UTF-8 encoding - - ```cadence - let a: Character = "a" - let a_bytes = a.utf8 // `a_bytes` is `[97]` - - let bouquet: Character = "\u{1F490}" - let bouquet_bytes = bouquet.utf8 // `bouquet_bytes` is `[240, 159, 146, 144]` - ``` - ## Arrays Arrays are mutable, ordered collections of values. @@ -1116,7 +1099,6 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun concat(_ array: T): T ``` @@ -1146,7 +1128,6 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun contains(_ element: T): Bool ``` @@ -1172,7 +1153,6 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun firstIndex(of: T): Int? ``` @@ -1194,7 +1174,6 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence - access(all) fun slice(from: Int, upTo: Int): [T] ``` @@ -1220,164 +1199,6 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. let invalidIndices = example.slice(from: 2, upTo: 1) ``` -- - ```cadence - access(all) - fun reverse(): [T] - ``` - - Returns a new array with contents in the reversed order. - Available if `T` is not resource-kinded. - - ```cadence - let example = [1, 2, 3, 4] - - // Create a new array which is the reverse of the original array. - let reversedExample = example.reverse() - // `reversedExample` is now `[4, 3, 2, 1]` - ``` - - ```cadence - access(all) - fun reverse(): [T; N] - ``` - - Returns a new fixed-sized array of same size with contents in the reversed order. - - ```cadence - let fixedSizedExample: [String; 3] = ["ABC", "XYZ", "PQR"] - - // Create a new array which is the reverse of the original array. - let fixedArrayReversedExample = fixedSizedExample.reverse() - // `fixedArrayReversedExample` is now `["PQR", "XYZ", "ABC"]` - ``` - -- - ```cadence - access(all) - fun map(_ f: fun(T): U): [U] - ``` - - Returns a new array whose elements are produced by applying the mapper function on each element - of the original array. - Available if `T` is not resource-kinded. - - ```cadence - let example = [1, 2, 3] - let trueForEven = - fun (_ x: Int): Bool { - return x % 2 == 0 - } - - let mappedExample: [Bool] = example.map(trueForEven) - // `mappedExample` is `[False, True, False]` - // `example` still remains as `[1, 2, 3]` - - // Invalid: Map using a function which accepts a different type. - // This results in a type error, as the array contains `Int` values while function accepts - // `Int64`. - let functionAcceptingInt64 = - fun (_ x: Int64): Bool { - return x % 2 == 0 - } - let invalidMapFunctionExample = example.map(functionAcceptingInt64) - ``` - - `map` function is also available for fixed-sized arrays: - - ```cadence - access(all) - fun map(_ f: fun(T): U): [U; N] - ``` - - Returns a new fixed-sized array whose elements are produced by applying the mapper function on - each element of the original array. - Available if `T` is not resource-kinded. - - ```cadence - let fixedSizedExample: [String; 3] = ["ABC", "XYZYX", "PQR"] - let lengthOfString = - fun (_ x: String): Int { - return x.length - } - - let fixedArrayMappedExample = fixedSizedExample.map(lengthOfString) - // `fixedArrayMappedExample` is now `[3, 5, 3]` - // `fixedSizedExample` still remains as ["ABC", "XYZYX", "PQR"] - - // Invalid: Map using a function which accepts a different type. - // This results in a type error, as the array contains `String` values while function accepts - // `Bool`. - let functionAcceptingBool = - fun (_ x: Bool): Int { - return 0 - } - let invalidMapFunctionExample = fixedSizedExample.map(functionAcceptingBool) - ``` - -- - ```cadence - access(all) - fun filter(_ f: fun(T): Bool): [T] - ``` - - Returns a new array whose elements are filtered by applying the filter function on each element - of the original array. - Available if `T` is not resource-kinded. - - ```cadence - let example = [1, 2, 3] - let trueForEven = - fun (_ x: Int): Bool { - return x % 2 == 0 - } - - let filteredExample: [Int] = example.filter(trueForEven) - // `filteredExample` is `[2]` - // `example` still remains as `[1, 2, 3]` - - // Invalid: Filter using a function which accepts a different type. - // This results in a type error, as the array contains `Int` values while function accepts - // `Int64`. - let functionAcceptingInt64 = - fun (_ x: Int64): Bool { - return x % 2 == 0 - } - let invalidFilterFunctionExample = example.filter(functionAcceptingInt64) - ``` - - `filter` function is also available for fixed-sized arrays: - - ```cadence - access(all) - fun filter(_ f: fun(T): Bool): [T] - ``` - - Returns a new **variable-sized** array whose elements are filtered by applying the filter function on each element - of the original array. - Available if `T` is not resource-kinded. - - ```cadence - let fixedSizedExample: [String; 3] = ["AB", "XYZYX", "PQR"] - let lengthOfStringGreaterThanTwo = - fun (_ x: String): Bool { - return x.length > 2 - } - - let fixedArrayFilteredExample = fixedSizedExample.filter(lengthOfStringGreaterThanTwo) - // `fixedArrayFilteredExample` is `["XYZYX", "PQR"]` - // `fixedSizedExample` still remains as ["AB", "XYZYX", "PQR"] - - // Invalid: Filter using a function which accepts a different type. - // This results in a type error, as the array contains `String` values while function accepts - // `Bool`. - let functionAcceptingBool = - fun (_ x: Bool): Bool { - return True - } - let invalidFilterFunctionExample = fixedSizedExample.filter(functionAcceptingBool) - ``` - #### Variable-size Array Functions The following functions can only be used on variable-sized arrays. @@ -1385,7 +1206,6 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun append(_ element: T): Void ``` @@ -1409,7 +1229,6 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun appendAll(_ array: T): Void ``` @@ -1434,7 +1253,6 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Insert) fun insert(at: Int, _ element: T): Void ``` @@ -1466,7 +1284,6 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence - access(Mutate | Remove) fun remove(at: Int): T ``` @@ -1492,7 +1309,6 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Remove) fun removeFirst(): T ``` @@ -1523,7 +1339,6 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence - access(Mutate | Remove) fun removeLast(): T ``` @@ -1706,7 +1521,6 @@ booleans[0] = true - ```cadence - access(Mutate | Insert) fun insert(key: K, _ value: V): V? ``` @@ -1773,7 +1587,6 @@ booleans[0] = true - ```cadence - access(Mutate | Remove) let keys: [K] ``` @@ -1814,7 +1627,6 @@ booleans[0] = true - ```cadence - access(all) fun containsKey(key: K): Bool ``` @@ -1840,8 +1652,7 @@ booleans[0] = true - ```cadence - access(all) - fun forEachKey(_ function: fun(K): Bool): Void + fun forEachKey(_ function: ((K): Bool)): Void ``` Iterate through all the keys in the dictionary, exiting early if the passed function returns false. diff --git a/docs/measuring-time.mdx b/docs/measuring-time.mdx index 4355086..0b23db7 100644 --- a/docs/measuring-time.mdx +++ b/docs/measuring-time.mdx @@ -26,7 +26,7 @@ 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](https://developers.flow.com/build/run-and-secure/nodes/faq/operators.mdx#does-the-blockheight-go-up-1-every-second). +as [the block rate naturally fluctuates](https://developers.flow.com/references/run-and-secure/nodes/faq/operators.mdx#does-the-blockheight-go-up-1-every-second). In addition to the natural variation described above, there are several theoretical block production attacks that could skew this relationship even further. diff --git a/docs/solidity-to-cadence.md b/docs/solidity-to-cadence.md index 4b69fa1..b047568 100644 --- a/docs/solidity-to-cadence.md +++ b/docs/solidity-to-cadence.md @@ -1,6 +1,5 @@ --- -title: Guide for Solidity Developers -sidebar_label: Guide for Solidity Developers +title: Guide for Solidity developers sidebar_position: 8 --- Cadence introduces a different way to approach smart contract development which may feel unfamiliar to @@ -13,7 +12,7 @@ up while transitioning. # Conceptual foundations for Cadence -A fundamental difference to get used to when adjusting to Cadence from Solidity is mindset. Security and +A fundamental difference to get used to when adjusting to Cadence from Solidity is of mindset. Security and interoperability on Ethereum are designed around addresses (or more specifically the account associated with an address), resulting in all contracts having to carefully track and evaluate access and authorizations. @@ -51,17 +50,14 @@ There is only one account type in Cadence also with an account address, similar Accounts realize ownership on Flow in being the container where keys, Resources, and contracts are stored on-chain. -## Account +### PublicAccount and AuthAccount -`Account` is the type that provides access to an account. +`PublicAccount` is the publicly available part of an account through which public functions or objects can be +accessed by any account using the `getAccount` function. -The `getAccount` function allows getting access to the publicly available functions and fields of an account. -For example, this allows querying an accounts balance. - -An authorized `Account` reference provides access and allows the management of, -for instance, the account's storage, keys configuration, and contract code. -An authorized `Account` reference can only be acquired by signing a transaction. -Capabilities ensure that resources held in an account can be safely shared/accessed. +`AuthAccount` is available only to the signer of the transaction. An `AuthAccount` reference provides full access +to that account's storage, keys configuration, and contract code. Capabilities ensure that resources held in an account can be +safely shared/accessed; access to `AuthAccount` should never be given to other accounts. ## Resources @@ -79,7 +75,7 @@ without any explicit handling needed. ## Capability-based access -Remote access to stored objects can be managed via [Capabilities](./language/capabilities.md). This +Remote access to stored objects can be managed via [Capabilities](./language#capability-based-access-control). This means that if an account wants to be able to access another account's stored objects, it must have been provided with a valid Capability to that object. Capabilities can be either public or private. An account can share a public Capability if it wants to give all other accounts access. (For example, it’s common for an account to accept fungible @@ -90,7 +86,7 @@ controls minting through an “administrator Capability” that grants specific ## Contract standards There are numerous widely-used contract standards established to benefit the ecosystem, for example -[Fungible Token](https://developers.flow.com/build/flow.md#flow-token)(FT) and [Non-Fungible Token](https://developers.flow.com/build/flow#overview)(NFT) +[Fungible Token](https://developers.flow.com/build/flow.md#flow-token)(FT) and [Non-Fungible Token](https://developers.flow.com/build/flow.md#flow-nft#overview)(NFT) standards which are conceptually equivalent to Ethereum's ERC-20 and ERC-721 standards. Cadence's object-oriented design means standards apply through contract sub-types such as Resources, Resource interfaces, or other types declared in the contract standard. Standards can define and limit behaviour and/or set conditions which @@ -154,12 +150,13 @@ the opposite direction than the [access-based security](https://en.wikipedia.org ## Access control using Capabilities -Solidity lacks specific types or other primitives to aid with permission management. -Developers have to inline guards to `require` at every function entry-point, -thus validating the `msg.sender` of the transaction. +Solidity lacks specific types or other primitives to aid with permission management. Developers have to inline +guards to `require` at every function entry-point, thus validating the `msg.sender` of the transaction. [Capabilities](./language/capabilities.md) are defined by linking storage paths (namespaces for contract -storage) to protected objects and then making that linked capability available to other accounts. +storage) to protected objects and then making that linked capability available to other accounts. Public and private +scopes defined for storage paths and Capabilities themselves align precisely with +[PublicAccount](./language/accounts.mdx#publicaccount) / [AuthAccount](./language/accounts.mdx#authaccount) account scopes. Any account can get access to an account's public Capabilities. Public capabilities are created using public paths, i.e. they have the domain `public`. For example, all accounts have a default public capability linked to the @@ -167,12 +164,13 @@ i.e. they have the domain `public`. For example, all accounts have a default pub interface, to allow any account to `borrow()` a reference to the Vault to make a `deposit()`. Since only the functions defined under the `[FungibleToken.Receiver](https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc#L105)` interface are exposed, the borrower of the vault reference cannot call `withdraw()` since it is scoped in the `provider` -interface. +interface. Public capabilities can be obtained from both authorized accounts (`AuthAccount`) and public accounts +(`PublicAccount`). Private capabilities are specifically granted to accounts. They are created using private paths, i.e. they have the domain `private`. After creation, they can be obtained from authorised account objects (`AuthAccount`) but not public accounts (`PublicAccount`). To share a private Capability with another account, the owning account must `publish` it -to another account which places in the [account inbox](./language/accounts/inbox.mdx). The recipient can later +to another account which places in the [account inbox](./language/accounts.mdx#account-inbox). The recipient can later claim the Capability from the account inbox using then `claim` function. Capabilities can be `unpublished` and can also be [revoked](./design-patterns.md#capability-revocation) by the creating @@ -260,7 +258,8 @@ Cadence provides an `assert` operator which mirrors `assert` in Solidity. Modifiers are extensively used in Solidity when enforcing pre-checks within a function. This is a powerful language feature. However, modifiers can also mutate state which introduces risks to program control flow. -Cadence uses `pre` and `post` blocks to validate input values or the function execution outputs. Notably, `pre` and `post` block prohibit changing of state and may only enforce conditions. +Cadence uses `pre` and `post` blocks to validate input values or the function execution outputs. Notably, `pre` and +`post` block prohibit changing of state and may only enforce conditions. Another difference is that modifiers in Solidity can be re-used within the contract multiple times. Cadence `pre` & `post` blocks are associated to individual functions only, reducing the likelihood of errors but which @@ -290,10 +289,10 @@ Scripts are read-only in nature, requiring only a `main` function declaration an import FungibleToken from "../../contracts/FungibleToken.cdc" import ExampleToken from "../../contracts/ExampleToken.cdc" -access(all) fun main(account: Address): UFix64 { +pub fun main(account: Address): UFix64 { let acct = getAccount(account) - let vaultRef = acct.capabilities - .borrow<&ExampleToken.Vault>(ExampleToken.VaultPublicPath) + let vaultRef = acct.getCapability(ExampleToken.VaultPublicPath) + .borrow<&ExampleToken.Vault{FungibleToken.Balance}>() ?? panic("Could not borrow Balance reference to the Vault") return vaultRef.balance @@ -319,7 +318,7 @@ transaction(addressAmountMap: {Address: UFix64}) { prepare(signer: AuthAccount) { // Get a reference to the signer's stored vault - self.vaultRef = signer.storage.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath) + self.vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath) ?? panic("Could not borrow reference to the owner's Vault!") } @@ -334,8 +333,8 @@ transaction(addressAmountMap: {Address: UFix64}) { let recipient = getAccount(address) // Get a reference to the recipient's Receiver - let receiverRef = recipient.capabilities - .borrow<&{FungibleToken.Receiver}>(ExampleToken.ReceiverPublicPath) + let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath) + .borrow<&{FungibleToken.Receiver}>() ?? panic("Could not borrow receiver reference to the recipient's Vault") // Deposit the withdrawn tokens in the recipient's receiver @@ -375,7 +374,7 @@ Cadence offers a wide range of options to implement various multi-signature sche - Inherent support for multi-sign transactions. - Resource transfer scheme. -- Inherent support of the BLS signature scheme. +- Inherent support of BLS signature scheme. Flow account keys have assigned weights, where a 1000 unit weight is the cumulative weight needed from signing keys to execute a transaction successfully. One can divide weights across multiple keys and distribute those partial @@ -462,7 +461,7 @@ program flow similar to `if nil; else; end;`. ## Iterable Dictionaries Solidity offers the mapping type, however, it is not iterable. Because of that dApp developers have to maintain -off-chain tracking to have access to keys. This also pushes builders to create custom datatypes like `EnumerableMap` +off-chain tracking to be have access to keys. This also pushes builders to create custom datatypes like `EnumerableMap` which adds to gas costs. Cadence offers the [Dictionary](./language/control-flow.md) type, an unordered collection of key-value associations @@ -482,7 +481,7 @@ provides: ## Argument labelling -Argument labels in Cadence help to disambiguate input values. They make code more readable and explicit. They +Argument labels in Cadence help to disambiguate input values. They makes code more readable and explicit. They also eliminate confusion around the order of arguments when working with the same type. They must be included in the function call. This restriction can be skipped if the label is preceded by `_ ` on its declaration. Eg: @@ -494,9 +493,4 @@ called as can be called as `self.foo(balance: 30.0)` or as -`self.foo(30.0)` - -## Additional resources - -* Cadence or Solidity: [On-Chain Token Transfer Deep Dive](https://flow.com/engineering-blogs/flow-blockchain-programming-language-smart-contract-cadence-solidity-comparison-ethereum) -* Implementing the [Bored Ape Yacht Club](https://flow.com/post/implementing-the-bored-ape-yacht-club-smart-contract-in-cadence) smart contract in Cadence \ No newline at end of file +`self.foo(30.0)` \ No newline at end of file diff --git a/docs/testing-framework.mdx b/docs/testing-framework.mdx index 541ac95..a5f6226 100644 --- a/docs/testing-framework.mdx +++ b/docs/testing-framework.mdx @@ -878,7 +878,7 @@ access(all) struct Transaction { } ``` -The number of authorizers must match the number of `&Account` parameters in the `prepare` block of the transaction. +The number of authorizers must match the number of `AuthAccount` arguments in the `prepare` block of the transaction. ```cadence import Test @@ -890,7 +890,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: &Account) {} execute{} }", + code: "transaction { prepare(acct: AuthAccount) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -906,7 +906,7 @@ access(all) fun testExample() { access(all) fun testExampleTwo() { let tx = Test.Transaction( - code: "transaction { prepare(acct: &Account) {} execute{} }", + code: "transaction { prepare(acct: AuthAccount) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -950,7 +950,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: &Account) {} execute{} }", + code: "transaction { prepare(acct: AuthAccount) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -1124,7 +1124,7 @@ access(all) fun testExample() { Test.expect(scriptResult, Test.beSucceeded()) let tx = Test.Transaction( - code: "transaction { prepare(acct: &Account) {} execute{} }", + code: "transaction { prepare(acct: AuthAccount) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -1272,7 +1272,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: &Account) {} execute{ log(\"in a transaction\") } }", + code: "transaction { prepare(acct: AuthAccount) {} execute{ log(\"in a transaction\") } }", authorizers: [account.address], signers: [account], arguments: [], diff --git a/docs/tutorial/01-first-steps.md b/docs/tutorial/01-first-steps.md index 0ae326a..8ba3a70 100644 --- a/docs/tutorial/01-first-steps.md +++ b/docs/tutorial/01-first-steps.md @@ -14,7 +14,7 @@ Cadence introduces new features to smart contract programming that help develope - Type safety and a strong static type system - Resource-oriented programming, a new paradigm that pairs linear types with object capabilities to create a secure and declarative model for digital ownership by ensuring that resources (and their associated assets) can only exist in one location at a time, cannot be copied, and cannot be accidentally lost or deleted -- Built-in pre-conditions and post-conditions for functions and [transactions](../language/transactions.md) +- Built-in pre-conditions and post-conditions for functions and [transactions](../language/transactions) - The utilization of capability-based security, which enforces access control by requiring that access to objects is restricted to only the owner and those who have a valid reference to the object @@ -62,7 +62,7 @@ to deploy that contract to the currently selected account. After a few seconds, the contract should deploy. In the accounts section, you should now see the name of the contract next to the selected account that you deployed too -and if you click on "Log" in the bottom section of the screen, you should +and if you click on "Log" in the bottom section of the scrren, you should see a message in the console confirming that the contract was deployed and which account it was deployed to. You can also select transactions and scripts from the left selection menu @@ -81,4 +81,4 @@ or you can use the pre-generated tutorial setups in the Playground. ## Say Hello, World! Now that you have the Flow Developer Playground running, -you can [create a smart contract](./02-hello-world.md) for Flow! +you can [create a smart contract](./02-hello-world.md) for Flow! \ No newline at end of file diff --git a/docs/tutorial/02-hello-world.md b/docs/tutorial/02-hello-world.md index de69a8c..0361e80 100644 --- a/docs/tutorial/02-hello-world.md +++ b/docs/tutorial/02-hello-world.md @@ -90,7 +90,7 @@ It also allows you to save and share your work with others so that you can test When you work with accounts in the Flow Playground, you start with five default accounts that you can change and reconfigure. Each account in your environment has a unique address, and you can select an account in the left toolbar, -which will open up the contracts that are saved for that account. +which will open up the contracts that are saved for that account. The `HelloWorld` contracts are loaded by default for each account unless you load an existing playground project with other saved contracts. @@ -143,44 +143,44 @@ Open the Account `0x01` tab with the file called ```cadence HelloWorld.cdc // HelloWorld.cdc // -access(all) contract HelloWorld { +pub contract HelloWorld { // Declare a public field of type String. // - // All fields must be initialized in the initializer. - access(all) let greeting: String + // All fields must be initialized in the init() function. + pub let greeting: String - // The initializer is required if the contract contains any fields. + // The init() function is required if the contract contains any fields. init() { self.greeting = "Hello, World!" } // Public function that returns our friendly greeting! - access(all) fun hello(): String { + pub fun hello(): String { return self.greeting } } ``` -The line `access(all) contract HelloWorld ` declares a contract that is accessible in all scopes (public). -It's followed by `access(all) let greeting: String` which declares a state constant (`let`) of type `String` that is accessible in all scopes(`access(all)`). +The line `pub contract HelloWorld ` declares a contract that is accessible in all scopes (public). +It's followed by `pub let greeting: String` which declares a state constant (`let`) of type `String` that is accessible in all scopes(`pub`). You would have used `var` to declare a variable, which means that the value can be changed later on instead of remaining constant like with `let`. -You can use `access(all)` and the `access(all)` keyword interchangeably. +You can use `access(all)` and the `pub` keyword interchangeably. They are both examples of an access control specification that means an interface can be accessed in all scopes, but not written to in all scopes. For more information about the different levels of access control permitted in Cadence, refer to the [Access Control section of the language reference](../language/access-control). The `init()` section is called the initializer. It is a special function that only runs when the contract is first created. Objects similar to contracts, such as other [composite types like structs or resources](../language/composite-types), -require that the initializer initializes all fields that are declared in a composite type. +require that the `init()` function initialize any fields that are declared in a composite type. In the above example, the initializer sets the `greeting` field to `"Hello, World!"` when the contract is initialized. The last part of our `HelloWorld` contract is a public function called `hello()`. This declaration returns a value of type `String`. Anyone who imports this contract in their transaction or script can read the public fields, -use the public types, and call the public contract functions; i.e. the ones that have `access(all)` or `access(all)` specified. +use the public types, and call the public contract functions; i.e. the ones that have `pub` or `access(all)` specified. Soon you'll deploy this contract to your account and run a transaction that calls its function, but first, let's look at what accounts and transactions are. @@ -266,7 +266,7 @@ import HelloWorld from 0x01 transaction { - prepare(acct: &Account) {} + prepare(acct: AuthAccount) {} execute { log(HelloWorld.hello()) diff --git a/docs/tutorial/03-resources.md b/docs/tutorial/03-resources.md index 3028c2f..42545e3 100644 --- a/docs/tutorial/03-resources.md +++ b/docs/tutorial/03-resources.md @@ -53,8 +53,8 @@ Resources are one of Cadence's defining features. In Cadence, resources are a composite type like a struct or a class, but with some special rules. Here is an example definition of a resource: ```cadence -access(all) resource Money { - access(all) let balance: Int +pub resource Money { + pub let balance: Int init() { self.balance = 0 @@ -84,7 +84,7 @@ To interact with resources, you'll learn a few important concepts: - Using the create keyword - The move operator `<-` -- The [Account Storage API](../language/accounts/storage.mdx) +- The [Account Storage API](../language/accounts#account-storage-api) Let's start by looking at how to create a resource with the `create` keyword and the move operator `<-`. @@ -103,21 +103,21 @@ Open the Account `0x01` tab with file named `HelloWorldResource.cdc`.
```cadence HelloWorldResource.cdc -access(all) contract HelloWorld { +pub contract HelloWorld { // Declare a resource that only includes one function. - access(all) resource HelloAsset { + pub resource HelloAsset { // A transaction can call this function to get the "Hello, World!" // message from the resource. - access(all) fun hello(): String { + pub fun hello(): String { return "Hello, World!" } } // We're going to use the built-in create function to create a new instance // of the HelloAsset resource - access(all) fun createHelloAsset(): @HelloAsset { + pub fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } @@ -135,8 +135,8 @@ Deploy this code to account `0x01` using the `Deploy` button. We start by declaring a new `HelloWorld` contract in account `0x01`, inside this new `HelloWorld` contract we: -1. Declare the resource `HelloAsset` with public scope `access(all)` -2. Declare the resource function `hello()` inside `HelloAsset` with public scope `access(all)` +1. Declare the resource `HelloAsset` with public scope `pub` +2. Declare the resource function `hello()` inside `HelloAsset` with public scope `pub` 3. Declare the contract function `createHelloAsset()` which `create`s a `HelloAsset` resource 4. The `createHelloAsset()` function uses the move operator (`<-`) to return the resource @@ -154,8 +154,8 @@ Let's walk through this contract in more detail, starting with the resource. Resources are one of the most important things that Cadence introduces to the smart contract design experience: ```cadence -access(all) resource HelloAsset { - access(all) fun hello(): String { +pub resource HelloAsset { + pub fun hello(): String { return "Hello, World!" } } @@ -179,18 +179,18 @@ init() { ``` All composite types like contracts, resources, -and structs can have an optional initializer that only runs when the object is initially created. +and structs can have an optional `init()` function that only runs when the object is initially created. Cadence requires that all fields must be explicitly initialized, so if the object has any fields, this function has to be used to initialize them. -Contracts also have read and write access to the storage of the account that they are deployed to -by using the built-in [`self.account`](../language/contracts.mdx) field. -This is an [account reference](../language/accounts/index.mdx) (`&Account`), -authorized to access and manage all aspects of the account, -such as account storage, keys, and contracts. +Contracts also have read and write access to the storage of the account that they are deployed to by using the built-in +[`self.account`](../language/contracts) object. +This is an [`AuthAccount` object](../language/accounts#authaccount) +that gives them access to many different functions to interact with the private storage of the account. + +This contract's `init` function is simple, it logs the phrase `"Hello Asset"` to the console. -This contract's initializer is simple, it logs the phrase `"Hello Asset"` to the console. **A resource can only be created in the scope that it is defined in.** @@ -200,7 +200,7 @@ This prevents anyone from being able to create arbitrary amounts of resource obj In this example, we declared a function that can create `HelloAsset` resources: ```cadence -access(all) fun createHelloAsset(): @HelloAsset { +pub fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } ``` @@ -260,12 +260,12 @@ import HelloWorld from 0x01 transaction { - prepare(acct: auth(SaveValue) &Account) { + prepare(acct: AuthAccount) { // Here we create a resource and move it to the variable newHello, // then we save it in the account storage let newHello <- HelloWorld.createHelloAsset() - acct.storage.save(<-newHello, to: /storage/HelloAssetTutorial) + acct.save(<-newHello, to: /storage/HelloAssetTutorial) } // In execute, we log a string to confirm that the transaction executed successfully. @@ -283,11 +283,13 @@ Here's what this transaction does: 4. `log` the text `HelloAsset created and stored` to the console. This is our first transaction using the `prepare` phase! -The `prepare` phase is the only place that has access to the signing accounts, -via [account references (`&Account`)](../language/accounts/index.mdx). -Account references have access to many different methods that are used to interact with account, e.g., the account's storage. -You can see the documentation for all of these in the [account section of the language reference](../language/accounts/index.mdx). -In this tutorial, we'll be using account functions to save to and load from account storage (`/storage/`). +The `prepare` phase is the only place that has access to the signing accounts' +[private `AuthAccount` object](../language/accounts#authaccount). +`AuthAccount` objects have many different methods that are used to interact with account storage. +You can see the documentation for all of these in the [account section of the language reference](../language/accounts#authaccount). +In this tutorial, we'll be using `AuthAccount` methods to save and load from `/storage/`. +The `prepare` phase can also create `/private/` and `/public/` links to the objects in `/storage/`, +called [capabilities](../language/capabilities) (more on these later). By not allowing the execute phase to access account storage, we can statically verify which assets and areas of the signers' storage a given transaction can modify. @@ -304,13 +306,13 @@ let newHello <- HelloWorld.createHelloAsset() ``` Next, we save the resource to the account storage. -We use the [account storage API](../language/accounts/storage.mdx) to interact with the account storage in Flow. +We use the [account storage API](../language/accounts#account-storage-api) to interact with the account storage in Flow. To save the resource, we'll be using the -[`save()`](../language/accounts/storage.mdx) +[`save()`](../language/accounts#account-storage-api) method from the account storage API to store the resource in the account at the path `/storage/HelloAssetTutorial`. ```cadence -acct.storage.save(<-newHello, to: /storage/HelloAssetTutorial) +acct.save(<-newHello, to: /storage/HelloAssetTutorial) ``` The first parameter to `save` is the object that is being stored, and the `to` parameter is the path that the object is being stored at. @@ -371,14 +373,14 @@ This should open a view of the different contracts and objects in the account. You should see this entry for the `HelloWorld` contract and the `HelloAsset` resource: ``` -Deployed Contracts: +Deployed Contracts: [ { "contract": "HelloWorld", "height": 6 } -] -Account Storage: +] +Account Storage: { "Private": null, "Public": {}, @@ -431,11 +433,11 @@ import HelloWorld from 0x01 transaction { - prepare(acct: auth(LoadValue, SaveValue) &Account) { + prepare(acct: AuthAccount) { // Load the resource from storage, specifying the type to load it as // and the path where it is stored - let helloResource <- acct.storage.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) + let helloResource <- acct.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) // We use optional chaining (?) because the value in storage // may or may not exist, and thus is considered optional. @@ -444,7 +446,7 @@ transaction { // Put the resource back in storage at the same spot // We use the force-unwrap operator `!` to get the value // out of the optional. It aborts if the optional is nil - acct.storage.save(<-helloResource!, to: /storage/HelloAssetTutorial) + acct.save(<-helloResource!, to: /storage/HelloAssetTutorial) } } ``` @@ -453,18 +455,18 @@ Here's what this transaction does: 1. Import the `HelloWorld` definitions from account `0x01` 2. Moves the `HelloAsset` object from storage to `helloResource` with the move operator - and the `load` function from the [account storage API](../language/accounts/storage.mdx) + and the `load` function from the [account storage API](../language/accounts#account-storage-api) 3. Calls the `hello()` function of the `HelloAsset` resource stored in `helloResource` and logs the result 4. Saves the resource in the account that we originally moved it from at the path `/storage/HelloAssetTutorial` -We're going to be using the `prepare` phase again to load the resource -using the [reference to the account](../language/accounts/index.mdx) that is passed in. +We're going to be using the `prepare` phase again to load the resource because it +has access to the signing accounts' [private `AuthAccount` object](../language/accounts#authaccount). Let's go over the transaction in more detail. -To remove an object from storage, we use the `load` method from the [account storage API](../language/accounts/storage.mdx) +To remove an object from storage, we use the `load` method from the [account storage API](../language/accounts#account-storage-api) ```cadence -let helloResource <- acct.storage.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) +let helloResource <- acct.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) ``` If no object of the specified type is stored under the given path, the function returns nothing, or `nil`. @@ -513,7 +515,7 @@ However, if the stored value was `nil`, the function call would not occur and th Next, we use `save` again to put the object back in storage in the same spot: ```cadence -acct.storage.save(<-helloResource!, to: /storage/HelloAssetTutorial) +acct.save(<-helloResource!, to: /storage/HelloAssetTutorial) ``` Remember, `helloResource` is still an optional, so we have to handle the possibility that it is `nil`. @@ -560,12 +562,12 @@ Now that you have completed the tutorial, you have the basic knowledge to write - Use the `prepare` phase of a transaction to load resources from account storage Feel free to modify the smart contract to create different resources, -experiment with the available [account storage API](../language/accounts/storage.mdx), +experiment with the available [account storage API](../language/accounts#account-storage-api), and write new transactions and scripts that execute different functions from your smart contract. Have a look at the [resource reference page](../language/resources) to find out more about what you can do with resources. You're on the right track to building more complex applications with Cadence, -now is a great time to check out the [Cadence Best Practices document](../design-patterns.md) -and [Anti-patterns document](../anti-patterns.md) +now is a great time to check out the [Cadence Best Practices document](../design-patterns) +and [Anti-patterns document](../anti-patterns) as your applications become more complex. diff --git a/docs/tutorial/04-capabilities.md b/docs/tutorial/04-capabilities.md index ffcebbb..69ee465 100644 --- a/docs/tutorial/04-capabilities.md +++ b/docs/tutorial/04-capabilities.md @@ -40,7 +40,7 @@ socialImageDescription: Capability smart contract image. This tutorial builds on the [previous `Resource` tutorial](./03-resources.md). Before beginning this tutorial, you should have an idea of how accounts,transactions,resources, and signers work with basic field types. This tutorial will build on your understanding of accounts and resources. -You'll learn how to interact with resources using [capabilities](../language/capabilities.md) +You'll learn how to interact with resources using [capabilities](../language/capabilities) In Cadence, resources are a composite type like a struct or a class, but with some special rules: - Each instance of a resource can only exist in exactly one location and cannot be copied. - Resources must be explicitly moved from one location to another when accessed. @@ -65,7 +65,7 @@ in this case, they could create a capability that gives the friend access to onl instead of having to give full control over. Or if a user authenticates a trading app for the first time, -they could ask the user for a capability object that allows +the can could ask the user for a capability object that allows the app to access the trading functionality of a user's account so that the app doesn't need to ask the user for a signature every time. @@ -88,21 +88,21 @@ Open the Account `0x01` tab with file named `HelloWorldResource.cdc`.
```cadence HelloWorldResource-2.cdc -access(all) contract HelloWorld { +pub contract HelloWorld { // Declare a resource that only includes one function. - access(all) resource HelloAsset { + pub resource HelloAsset { // A transaction can call this function to get the "Hello, World!" // message from the resource. - access(all) fun hello(): String { + pub fun hello(): String { return "Hello, World!" } } // We're going to use the built-in create function to create a new instance // of the HelloAsset resource - access(all) fun createHelloAsset(): @HelloAsset { + pub fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } @@ -210,12 +210,12 @@ You might be confused that we were able to call a method on the `HelloAsset` obj without actually being directly in control of it! It is also stored in the `/storage/` domain of the account, which should be private. -This is because we created a [**capability**](../language/capabilities.md) for the `HelloAsset` object. +This is because we created a [**capability**](../language/capabilities) for the `HelloAsset` object. Capabilities are kind of like pointers in other languages, but which much more fine-grained control. ### Capability Based Access Control -[Capabilities](../language/capabilities.md) allow the owners of objects +[Capabilities](../language/capabilities) allow the owners of objects to specify what functionality of their private objects is available to others. Think of it kind of like an account's API, if you're familiar with the concept. The account owner has private objects stored in their storage, like their collectibles or their money, @@ -253,7 +253,7 @@ The `HelloAsset` object is stored in `/storage/HelloAssetTutorial`, which only t They want any user in the network to be able to call the `hello()` method. So they make a public capability in `/public/HelloAssetTutorial`. To create a capability, we use the `AuthAccount.link` method to link a new capability to an object in storage. -The type contained in `<>` is the reference type that the capability represents. +The type contained in `<>` is the restricted reference type that the capability represents. The capability says that whoever borrows a reference from this capability can only have access to the fields and methods that are specified by the type in `<>`. The specified type has to be a subtype of the type of the object being linked to, @@ -293,7 +293,7 @@ Only one reference to an object can exist at a time, so this type of vulnerabili Additionally, the owner of an object can effectively revoke capabilities they have created by moving the underlying object or destroying the link with the `unlink` method. If the referenced object is moved or the link is destroyed, capabilities that have been created from that link are invalidated. -You can find more [detailed documentation about capabilities in the language reference.](../language/capabilities.md) +You can find more [detailed documentation about capabilities in the language reference.](../language/capabilities) Now, anyone can call the `hello()` method on your `HelloAsset` object by borrowing a reference with your public capability in `/public/Hello`! (Covered in the next section) @@ -317,7 +317,7 @@ In the next section, we look at how capabilities can expand the access a script A script is a very simple transaction type in Cadence that cannot perform any writes to the blockchain and can only read the state of an account or contract. -To execute a script, write a function called `access(all) fun main()`. +To execute a script, write a function called `pub fun main()`. You can click the execute script button to run the script. The result of the script will be printed to the console output. @@ -334,15 +334,17 @@ Open the file `Script1.cdc`. ```cadence Script1.cdc import HelloWorld from 0x01 -access(all) fun main() { +pub fun main() { // Cadence code can get an account's public account object // by using the getAccount() built-in function. let helloAccount = getAccount(0x01) - // Borrow the public capability from the public path of the owner's account - let helloReference = helloAccount.capabilities - .borrow<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) + // Get the public capability from the public path of the owner's account + let helloCapability = helloAccount.getCapability<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) + + // borrow a reference for the capability + let helloReference = helloCapability.borrow() ?? panic("Could not borrow a reference to the hello capability") // The log built-in function logs its argument to stdout. @@ -355,31 +357,36 @@ access(all) fun main() { ``` Here's what this script does: -1. It gets an `Account` reference with `getAccount` and assigns it to the variable `helloAccount` -2. Borrows a reference using the `borrow` method for the capability from the `Create Link` transaction and assigns it to `helloReference` -3. Logs the result of the `hello()` function from `helloReference` to the console. +1. It fetches the `PublicAccount` object with `getAccount` and assigns it to the variable `helloAccount` +2. Uses the `getCapability` method to get the capability from the `Create Link` transaction +3. Borrows a reference for the capability using the `borrow` method and assigns it to `helloReference` +4. Logs the result of the `hello()` function from `helloReference` to the console. ```cadence let helloAccount = getAccount(0x01) ``` -The `Account` reference is available to anyone in the network for every account, +The `PublicAccount` object is available to anyone in the network for every account, but only has access to a small subset of functions that can be read from the `/public/` domain in an account. -Then, the script borrows the capability that was created in `Create Link`. +Then, the script gets the capability that was created in `Create Link`. ```cadence -// Borrow the public capability from the public path of the owner's account -let helloReference = helloAccount.capabilities - .borrow<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) - ?? panic("Could not borrow a reference to the hello capability") +// Get the public capability from the public path of the owner's account +let helloCapability = helloAccount.getCapability(/public/HelloAssetTutorial) ``` -To borrow a capability that is stored in an account, use the `account.capabilities.borrow()` function. -`borrow()` returns a reference to the storage object that the capability targets. -The borrow will fail if the capability does not exist, -the capabilities target storage path does not store a value, -or the value cannot be borrowed with the given type. +To get a capability that is stored in an account, use the `account.getCapability()` function. +This function is available on `AuthAccount`s and on `PublicAccount`s. +`getCapability()` returns a capability for the link at the path that is specified, +also with the type that is specified. +It does not check if the target exists, so the borrow will fail if the capability is invalid. + +After that, the script borrows a reference from the capability. + +```cadence +let helloReference = helloCapability.borrow() +``` Then, the script uses the reference to call the `hello()` function and prints the result. @@ -436,11 +443,11 @@ Now that you have completed the tutorial, you have the basic knowledge to write - Interact with resources using both signed transactions and scripts Feel free to modify the smart contract to create different resources, -experiment with the available [account storage API](../language/accounts/storage.mdx), +experiment with the available [account storage API](../language/accounts#account-storage-api), and write new transactions and scripts that execute different functions from your smart contract. -Have a look at the [capability-based access control page](../language/capabilities.md) +Have a look at the [capability-based access control page](../language/capabilities) to find out more about what you can do with capabilities. You're on the right track to building more complex applications with Cadence, -now is a great time to check out the [Cadence Best Practices document](../design-patterns.md) -and [Anti-patterns document](../anti-patterns.md) as your applications become more complex. +now is a great time to check out the [Cadence Best Practices document](../design-patterns) +and [Anti-patterns document](../anti-patterns) as your applications become more complex. diff --git a/docs/tutorial/05-non-fungible-tokens-1.md b/docs/tutorial/05-non-fungible-tokens-1.md index 7d8a459..debcce6 100644 --- a/docs/tutorial/05-non-fungible-tokens-1.md +++ b/docs/tutorial/05-non-fungible-tokens-1.md @@ -31,10 +31,10 @@ In this tutorial, we're going to deploy, store, and transfer **Non-Fungible Toke Open the starter code for this tutorial in the Flow Playground: - https://play.onflow.org/a21087ad-b22c-4981-b49e-17297e916fa6 - +
The tutorial will ask you to take various actions to interact with this code. @@ -128,11 +128,13 @@ We'll start by looking at a basic NFT contract, that adds an NFT to an account. The contract will: 1. Create a smart contract with the NFT resource type. -2. Declare an ID field, a metadata field and an initializer in the NFT resource. -3. Create an initializer for the contract that saves an NFT to an account. +2. Declare an ID field, a metadata field and an `init()` function in the NFT resource +3. Create an `init()` function for the contract that saves an NFT to an account + +This contract relies on the [account storage API](../language/accounts#authaccount) to save NFTs in the +`AuthAccount` object. -This contract relies on the [account storage API](../language/accounts/storage.mdx) -to save NFTs in the account. +--- @@ -153,17 +155,17 @@ Open Account `0x01` to see `BasicNFT.cdc`. ```cadence BasicNFT.cdc -access(all) contract BasicNFT { +pub contract BasicNFT { // Declare the NFT resource type - access(all) resource NFT { + pub resource NFT { // The unique ID that differentiates each NFT - access(all) let id: UInt64 + pub let id: UInt64 // String mapping to hold metadata - access(all) var metadata: {String: String} + pub var metadata: {String: String} - // Initialize both fields in the initializer + // Initialize both fields in the init function init(initID: UInt64) { self.id = initID self.metadata = {} @@ -171,13 +173,13 @@ access(all) contract BasicNFT { } // Function to create a new NFT - access(all) fun createNFT(id: UInt64): @NFT { + pub fun createNFT(id: UInt64): @NFT { return <-create NFT(initID: id) } // Create a single new NFT and save it to account storage init() { - self.account.storage.save(<-create NFT(initID: 1), to: /storage/BasicNFTPath) + self.account.save<@NFT>(<-create NFT(initID: 1), to: /storage/BasicNFTPath) } } ``` @@ -193,14 +195,15 @@ that can allow the storage of complex file formats and other such data. An NFT could even own other NFTs! This functionality is shown in the next tutorial. -In the contract's initializer, we create a new NFT object and move it into the account storage. +In the contract's `init` function, we create a new NFT object and move it into the account storage. ```cadence // put it in storage -self.account.storage.save(<-create NFT(initID: 1), to: /storage/BasicNFTPath) +self.account.save<@NFT>(<-create NFT(initID: 1), to: /storage/BasicNFTPath) ``` -Here we access the storage object of the account that the contract is deployed to and call its `save` method. +Here we access the `AuthAccount` object on the account the contract is deployed to and call its `save` method, +specifying `@NFT` as the type it is being saved as. We also create the NFT in the same line and pass it as the first argument to `save`. We save it to the `/storage` domain, where objects are meant to be stored. @@ -225,8 +228,8 @@ import BasicNFT from 0x01 // This transaction checks if an NFT exists in the storage of the given account // by trying to borrow from it. If the borrow succeeds (returns a non-nil value), the token exists! transaction { - prepare(acct: auth(BorrowValue) &Account) { - if acct.storage.borrow<&BasicNFT.NFT>(from: /storage/BasicNFTPath) != nil { + prepare(acct: AuthAccount) { + if acct.borrow<&BasicNFT.NFT>(from: /storage/BasicNFTPath) != nil { log("The token exists!") } else { log("No token found!") @@ -265,10 +268,7 @@ import BasicNFT from 0x01 /// to transfer an NFT transaction { - prepare( - signer1: auth(LoadValue) &Account, - signer2: auth(SaveValue) &Account - ) { + prepare(signer1: AuthAccount, signer2: AuthAccount) { // Fill in code here to load the NFT from signer1 // and save it into signer2's storage @@ -311,17 +311,14 @@ import BasicNFT from 0x01 /// to transfer an NFT transaction { - prepare( - signer1: auth(LoadValue) &Account, - signer2: auth(SaveValue) &Account - ) { + prepare(signer1: AuthAccount, signer2: AuthAccount) { // Load the NFT from signer1's account - let nft <- signer1.storage.load<@BasicNFT.NFT>(from: /storage/BasicNFTPath) + let nft <- signer1.load<@BasicNFT.NFT>(from: /storage/BasicNFTPath) ?? panic("Could not load NFT") // Save the NFT to signer2's account - signer2.storage.save(<-nft, to: /storage/BasicNFTPath) + signer2.save<@BasicNFT.NFT>(<-nft, to: /storage/BasicNFTPath) } } diff --git a/docs/tutorial/05-non-fungible-tokens-2.md b/docs/tutorial/05-non-fungible-tokens-2.md index e79e35f..ec6a707 100644 --- a/docs/tutorial/05-non-fungible-tokens-2.md +++ b/docs/tutorial/05-non-fungible-tokens-2.md @@ -30,8 +30,8 @@ a full implementation for **Non-Fungible Tokens (NFTs)**. Open the starter code for this tutorial in the Flow Playground: - https://play.onflow.org/f08e8e0d-d28e-4cbe-8d72-3afe2349c629 @@ -71,7 +71,7 @@ let newNFT <- BasicNFT.createNFT(id: 1) myNFTs[newNFT.id] <- newNFT // Save the NFT to a new storage path -account.storage.save(<-myNFTs, to: /storage/basicNFTDictionary) +account.save(<-myNFTs, to: /storage/basicNFTDictionary) ``` @@ -82,7 +82,7 @@ This example uses a [**Dictionary**: a mutable, unordered collection of key-valu ```cadence // Keys are `Int` // Values are `NFT` -access(all) let myNFTs: @{Int: NFT} +pub let myNFTs: @{Int: NFT} ``` In a dictionary, all keys must have the same type, and all values must have the same type. @@ -112,7 +112,7 @@ This contract expands on the `BasicNFT` we looked at by adding: 4. The `Collection` will declare fields and functions to interact with it, including `ownedNFTs`, `init()`, `withdraw()`, `destroy()`, and other important functions 5. Next, the contract declares functions that create a new NFT (`mintNFT()`) and an empty collection (`createEmptyCollection()`) -7. Finally, the contract declares an initializer that initializes the path fields, +7. Finally, the contract declares an `init()` function that initializes the path fields, creates an empty collection as well as a reference to it, and saves a minter resource to account storage. @@ -137,24 +137,24 @@ It contains what was already in `BasicNFT.cdc` plus additional resource declarat // // Learn more about non-fungible tokens in this tutorial: https://developers.flow.com/cadence/tutorial/non-fungible-tokens-1 -access(all) contract ExampleNFT { +pub contract ExampleNFT { // Declare Path constants so paths do not have to be hardcoded // in transactions and scripts - access(all) let CollectionStoragePath: StoragePath - access(all) let CollectionPublicPath: PublicPath - access(all) let MinterStoragePath: StoragePath + pub let CollectionStoragePath: StoragePath + pub let CollectionPublicPath: PublicPath + pub let MinterStoragePath: StoragePath // Tracks the unique IDs of the NFT - access(all) var idCount: UInt64 + pub var idCount: UInt64 // Declare the NFT resource type - access(all) resource NFT { + pub resource NFT { // The unique ID that differentiates each NFT - access(all) let id: UInt64 + pub let id: UInt64 - // Initialize both fields in the initializer + // Initialize both fields in the init function init(initID: UInt64) { self.id = initID } @@ -164,21 +164,21 @@ access(all) contract ExampleNFT { // to create public, restricted references to their NFT Collection. // They would use this to publicly expose only the deposit, getIDs, // and idExists fields in their Collection - access(all) resource interface NFTReceiver { + pub resource interface NFTReceiver { - access(all) fun deposit(token: @NFT) + pub fun deposit(token: @NFT) - access(all) fun getIDs(): [UInt64] + pub fun getIDs(): [UInt64] - access(all) fun idExists(id: UInt64): Bool + pub fun idExists(id: UInt64): Bool } // The definition of the Collection resource that // holds the NFTs that a user owns - access(all) resource Collection: NFTReceiver { + pub resource Collection: NFTReceiver { // dictionary of NFT conforming tokens // NFT is a resource type with an `UInt64` ID field - access(all) var ownedNFTs: @{UInt64: NFT} + pub var ownedNFTs: @{UInt64: NFT} // Initialize the NFTs field to an empty collection init () { @@ -189,7 +189,7 @@ access(all) contract ExampleNFT { // // Function that removes an NFT from the collection // and moves it to the calling context - access(all) fun withdraw(withdrawID: UInt64): @NFT { + pub fun withdraw(withdrawID: UInt64): @NFT { // If the NFT isn't found, the transaction panics and reverts let token <- self.ownedNFTs.remove(key: withdrawID)! @@ -200,7 +200,7 @@ access(all) contract ExampleNFT { // // Function that takes a NFT as an argument and // adds it to the collections dictionary - access(all) fun deposit(token: @NFT) { + pub fun deposit(token: @NFT) { // add the new token to the dictionary with a force assignment // if there is already a value at that key, it will fail and revert self.ownedNFTs[token.id] <-! token @@ -208,12 +208,12 @@ access(all) contract ExampleNFT { // idExists checks to see if a NFT // with the given ID exists in the collection - access(all) fun idExists(id: UInt64): Bool { + pub fun idExists(id: UInt64): Bool { return self.ownedNFTs[id] != nil } // getIDs returns an array of the IDs that are in the collection - access(all) fun getIDs(): [UInt64] { + pub fun getIDs(): [UInt64] { return self.ownedNFTs.keys } @@ -223,7 +223,7 @@ access(all) contract ExampleNFT { } // creates a new empty Collection resource and returns it - access(all) fun createEmptyCollection(): @Collection { + pub fun createEmptyCollection(): @Collection { return <- create Collection() } @@ -231,7 +231,7 @@ access(all) contract ExampleNFT { // // Function that mints a new NFT with a new ID // and returns it to the caller - access(all) fun mintNFT(): @NFT { + pub fun mintNFT(): @NFT { // create a new NFT var newNFT <- create NFT(initID: self.idCount) @@ -251,11 +251,10 @@ access(all) contract ExampleNFT { self.idCount = 1 // store an empty NFT Collection in account storage - self.account.storage.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath) + self.account.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath) - // publish a capability to the Collection in storage - let cap = self.account.capabilities.storage.issue<&{NFTReceiver}>(self.CollectionStoragePath) - self.account.capabilities.publish(cap, at: self.CollectionPublicPath) + // publish a reference to the Collection in storage + self.account.link<&{NFTReceiver}>(self.CollectionPublicPath, target: self.CollectionStoragePath) } } ``` @@ -301,10 +300,10 @@ destroy() { } ``` -When the `Collection` resource is created, the initializer is run +When the `Collection` resource is created, the `init` function is run and must explicitly initialize all member variables. This helps prevent issues in some smart contracts where uninitialized fields can cause bugs. -The initializer can never run again after this. +The init function can never run again after this. Here, we initialize the dictionary as a resource type with an empty dictionary. ```cadence @@ -318,7 +317,7 @@ of the keys of the dictionary using the built-in `keys` function. ```cadence // getIDs returns an array of the IDs that are in the collection -access(all) fun getIDs(): [UInt64] { +pub fun getIDs(): [UInt64] { return self.ownedNFTs.keys } ``` @@ -353,7 +352,7 @@ a resource can have more control over the resources it owns than the actual person whose account it is stored in! You'll encounter more fascinating implications of ownership and interoperability -like this as you get deeper into Cadence. +like this as you get deeper into Cadence. Now, back to the tutorial! @@ -362,7 +361,7 @@ Now, back to the tutorial! In the NFT Collection, all the functions and fields are public, but we do not want everyone in the network to be able to call our `withdraw` function. This is where Cadence's second layer of access control comes in. -Cadence utilizes [capability security](../language/capabilities.md), +Cadence utilizes [capability security](../language/capabilities), which means that for any given object, a user is allowed to access a field or method of that object if they either: - Are the owner of the object @@ -374,13 +373,13 @@ is only accessible by its owner. To give external accounts access to the `deposi the `getIDs` function, and the `idExists` function, the owner creates an interface that only includes those fields: ```cadence -access(all) resource interface NFTReceiver { +pub resource interface NFTReceiver { - access(all) fun deposit(token: @NFT) + pub fun deposit(token: @NFT) - access(all) fun getIDs(): [UInt64] + pub fun getIDs(): [UInt64] - access(all) fun idExists(id: UInt64): Bool + pub fun idExists(id: UInt64): Bool } ``` @@ -392,7 +391,7 @@ or they could put in the `/public/` domain of their account so that anyone can a If a user tried to use this capability to call the `withdraw` function, it wouldn't work because it doesn't exist in the interface that was used to create the capability. -The creation of the link and capability is seen in the `ExampleNFT.cdc` contract initializer +The creation of the link and capability is seen in the `ExampleNFT.cdc` contract `init()` function ```cadence // publish a reference to the Collection in storage @@ -425,14 +424,16 @@ Open the script file named `Print 0x01 NFTs`. import ExampleNFT from 0x01 // Print the NFTs owned by account 0x01. -access(all) fun main() { +pub fun main() { // Get the public account object for account 0x01 let nftOwner = getAccount(0x01) - // Find the public Receiver capability for their Collection and borrow it - let receiverRef = nftOwner.capabilities - .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) - ?? panic("Could not borrow receiver reference") + // Find the public Receiver capability for their Collection + let capability = nftOwner.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + + // borrow a reference from the capability + let receiverRef = capability.borrow() + ?? panic("Could not borrow receiver reference") // Log the NFTs that they own as an array of IDs log("Account 1 NFTs") @@ -490,8 +491,8 @@ transaction { prepare(acct: AuthAccount) { // Get the owner's collection capability and borrow a reference - self.receiverRef = acct.capabilities - .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + self.receiverRef = acct.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + .borrow() ?? panic("Could not borrow receiver reference") } @@ -518,7 +519,7 @@ This prints a list of the NFTs that account `0x01` owns. import ExampleNFT from 0x01 // Print the NFTs owned by account 0x01. -access(all) fun main() { +pub fun main() { // Get the public account object for account 0x01 let nftOwner = getAccount(0x01) @@ -561,19 +562,18 @@ import ExampleNFT from 0x01 // to use the NFT contract by creating a new empty collection, // storing it in their account storage, and publishing a capability transaction { - prepare(acct: auth(SaveValue, StorageCapabilities) &Account) { + prepare(acct: AuthAccount) { // Create a new empty collection let collection <- ExampleNFT.createEmptyCollection() // store the empty NFT Collection in account storage - acct.storage.save(<-collection, to: ExampleNFT.CollectionStoragePath) + acct.save<@ExampleNFT.Collection>(<-collection, to: ExampleNFT.CollectionStoragePath) log("Collection created for account 2") // create a public capability for the Collection - let cap = acct.capabilities.storage.issue<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionStoragePath) - acct.capabilities.publish(cap, at: ExampleNFT.CollectionPublicPath) + acct.link<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath) log("Capability created") } @@ -601,11 +601,10 @@ transaction { // transferred to the other account let transferToken: @ExampleNFT.NFT - prepare(acct: auth(BorrowValue) &Account) { + prepare(acct: AuthAccount) { // Borrow a reference from the stored collection - let collectionRef = acct.storage - .borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) + let collectionRef = acct.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) ?? panic("Could not borrow a reference to the owner's collection") // Call the withdraw function on the sender's Collection @@ -619,8 +618,8 @@ transaction { // Get the Collection reference for the receiver // getting the public capability and borrowing a reference from it - let receiverRef = recipient.capabilities - .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + let receiverRef = recipient.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + .borrow() ?? panic("Could not borrow receiver reference") // Deposit the NFT in the receivers collection @@ -643,7 +642,7 @@ Execute the script `Print all NFTs` to see the tokens in each account: import ExampleNFT from 0x01 // Print the NFTs owned by accounts 0x01 and 0x02. -access(all) fun main() { +pub fun main() { // Get both public account objects let account1 = getAccount(0x01) diff --git a/docs/tutorial/06-fungible-tokens.md b/docs/tutorial/06-fungible-tokens.md index 4ac8c8c..8f65e73 100644 --- a/docs/tutorial/06-fungible-tokens.md +++ b/docs/tutorial/06-fungible-tokens.md @@ -236,7 +236,7 @@ init(balance: UFix64) { } ``` -If you remove the `init` function from your `ExampleToken` contract, it will cause an error because +If you remove the initializer from your `ExampleToken` contract, it will cause an error because the balance field is no longer initialized. ### Deposit @@ -396,9 +396,9 @@ access(all) contract BasicToken { return <-create Vault(balance: 30.0) } - // The init function for the contract. All fields in the contract must - // be initialized at deployment. This is just an example of what - // an implementation could do in the init function. The numbers are arbitrary. + // The initializer for the contract. + // All fields in the contract must be initialized at deployment. + // This is just an example of what an implementation could do in the init initializer. init() { // create the Vault with the initial balance and put it in storage // account.save saves an object to the specified `to` path @@ -406,7 +406,7 @@ access(all) contract BasicToken { // The domain must be `storage`, `private`, or `public` // the identifier can be any name let vault <- self.createVault() - self.account.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) + self.account.storage.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) } } ``` @@ -422,7 +422,7 @@ Click the `Deploy` button at the top right of the editor to deploy the code. This deployment stores the contract for the basic fungible token in the selected account (account `0x01`) so that it can be imported into transactions. -A contract's `init` function runs at contract creation, and never again afterwards. +A contract's initializer runs at contract creation, and never again afterwards. In our example, this function stores an instance of the `Vault` object with an initial balance of 30. ```cadence @@ -432,7 +432,7 @@ In our example, this function stores an instance of the `Vault` object with an i // The domain must be `storage`, `private`, or `public` // the identifier can be any name let vault <- self.createVault() -self.account.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) +self.account.storage.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) ``` This line saves the new `@Vault` object to storage. @@ -475,10 +475,10 @@ import BasicToken from 0x01 transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(BorrowValue) &Account) { // withdraw tokens from your vault by borrowing a reference to it // and calling the withdraw function with that reference - let vaultRef = acct.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) + let vaultRef = acct.storage.borrow(from: /storage/CadenceFungibleTokenTutorialVault) ?? panic("Could not borrow a reference to the owner's vault") let temporaryVault <- vaultRef.withdraw(amount: 10.0) @@ -507,7 +507,7 @@ you can borrow a reference directly from an object in storage. ```cadence // Borrow a reference to the stored, private Vault resource -let vaultRef = acct.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) +let vaultRef = acct.storage.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) ?? panic("Could not borrow a reference to the owner's vault") ``` @@ -521,7 +521,7 @@ Capabilities allow us to accomplish this safely. --- -Another important feature in Cadence is its utilization of [**Capability-Based Security.**](../language/capabilities.md) +Another important feature in Cadence is its utilization of [**Capability-Based Security.**](../language/capabilities) This feature ensures that while the withdraw function is declared public on the resource, no one except the intended user and those they approve of can withdraw tokens from their vault. @@ -604,7 +604,7 @@ fields private unless it is explicitly needed to be public. This is one of THE MOST COMMON security mistakes that Cadence developers make, so it is vitally important to be aware of this. - See the [Cadence Best Practices document](../anti-patterns.md#array-or-dictionary-fields-should-be-private) for more details. + See the [Cadence Best Practices document](../anti-patterns#array-or-dictionary-fields-should-be-private) for more details. ## Adding Interfaces to Our Fungible Token @@ -630,10 +630,10 @@ This method for authorization can be used in many different ways and further decentralizes the control of the contract. We also store the `VaultMinter` object to `/storage/` -in the `init()` function in the same way as the vault, but in a different storage path: +in the initializer in the same way as the vault, but in a different storage path: ```cadence -self.account.save(<-create VaultMinter(), to: /storage/CadenceFungibleTokenTutorialMinter) +self.account.storage<-create VaultMinter(), to: /storage/CadenceFungibleTokenTutorialMinter) ``` Now is an important time to remind you that account storage not namespaced by contract, @@ -834,7 +834,7 @@ transaction { let vaultA <- ExampleToken.createEmptyVault() // Store the vault in the account storage - acct.save<@ExampleToken.Vault>(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) + acct.storage.save(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) log("Empty Vault stored") @@ -1126,5 +1126,5 @@ From here, you could try to extend the functionality of fungible tokens by makin ## Create a Flow Marketplace --- -Now that you have an understanding of how fungible tokens work on Flow and have a working NFT, you can learn how to create +Now that you have an understanding of how fungible tokens work on Flow and have a working NFT, you can learn how to create a marketplace that uses both fungible tokens and NFTs. Move on to the next tutorial to learn about Marketplaces in Cadence! diff --git a/docs/tutorial/07-marketplace-setup.md b/docs/tutorial/07-marketplace-setup.md index 09eb148..2cfc9d6 100644 --- a/docs/tutorial/07-marketplace-setup.md +++ b/docs/tutorial/07-marketplace-setup.md @@ -13,8 +13,8 @@ for an example of a production ready marketplace that you can use right now on t --- - Open the starter code for this tutorial in the Flow Playground: - @@ -61,7 +61,7 @@ transaction { log("Created Vault references") // store an empty NFT Collection in account storage - acct.storage.save(<-ExampleNFT.createEmptyCollection(), to: /storage/nftTutorialCollection) + acct.save<@ExampleNFT.Collection>(<-ExampleNFT.createEmptyCollection(), to: /storage/nftTutorialCollection) // publish a capability to the Collection in storage acct.link<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath) @@ -93,7 +93,7 @@ transaction { let vaultA <- ExampleToken.createEmptyVault() // Store the vault in the account storage - acct.storage.save(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) + acct.save<@ExampleToken.Vault>(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) // Create a public Receiver capability to the Vault let ReceiverRef = acct.link<&ExampleToken.Vault{ExampleToken.Receiver, ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver, target: /storage/CadenceFungibleTokenTutorialVault) @@ -177,7 +177,7 @@ import ExampleNFT from 0x02 // // Account 0x01: Vault Balance = 40, NFT.id = 1 // Account 0x02: Vault Balance = 20, No NFTs -access(all) fun main() { +pub fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) diff --git a/docs/tutorial/08-marketplace-compose.md b/docs/tutorial/08-marketplace-compose.md index f69b9e5..3ba9987 100644 --- a/docs/tutorial/08-marketplace-compose.md +++ b/docs/tutorial/08-marketplace-compose.md @@ -11,8 +11,8 @@ This contract is already deployed to testnet and mainnet and can be used by anyo --- - Open the starter code for this tutorial in the Flow Playground: - @@ -41,7 +41,7 @@ Flow is designed to enable composability because of the way that interfaces, res - [Interfaces](../language/interfaces) allow projects to support any generic type as long as it supports a standard set of functionality specified by an interface. - [Resources](../language/resources) can be passed around and owned by accounts, contracts or even other resources, unlocking different use cases depending on where the resource is stored. -- [Capabilities](../language/capabilities.md) allow exposing user-defined sets of functionality through special objects that enforce strict security with Cadence's type system. +- [Capabilities](../language/capabilities) allow exposing user-defined sets of functionality through special objects that enforce strict security with Cadence's type system. The combination of these allows developers to do more with less, re-using known safe code and design patterns to create new, powerful, and unique interactions! @@ -110,7 +110,7 @@ import ExampleNFT from 0x02 // // Account 0x01: Vault Balance = 40, NFT.id = 1 // Account 0x02: Vault Balance = 20, No NFTs -access(all) fun main() { +pub fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) @@ -219,27 +219,27 @@ import ExampleNFT from 0x02 // // https://github.com/onflow/nft-storefront -access(all) contract ExampleMarketplace { +pub contract ExampleMarketplace { // Event that is emitted when a new NFT is put up for sale - access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) + pub event ForSale(id: UInt64, price: UFix64, owner: Address?) // Event that is emitted when the price of an NFT changes - access(all) event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) + pub event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) // Event that is emitted when a token is purchased - access(all) event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) + pub event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) // Event that is emitted when a seller withdraws their NFT from the sale - access(all) event SaleCanceled(id: UInt64, seller: Address?) + pub event SaleCanceled(id: UInt64, seller: Address?) // Interface that users will publish for their Sale collection // that only exposes the methods that are supposed to be public // - access(all) resource interface SalePublic { - access(all) fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) - access(all) fun idPrice(tokenID: UInt64): UFix64? - access(all) fun getIDs(): [UInt64] + pub resource interface SalePublic { + pub fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) + pub fun idPrice(tokenID: UInt64): UFix64? + pub fun getIDs(): [UInt64] } // SaleCollection @@ -247,7 +247,7 @@ access(all) contract ExampleMarketplace { // NFT Collection object that allows a user to put their NFT up for sale // where others can send fungible tokens to purchase it // - access(all) resource SaleCollection: SalePublic { + pub resource SaleCollection: SalePublic { /// A capability for the owner's collection access(self) var ownerCollection: Capability<&ExampleNFT.Collection> @@ -278,7 +278,7 @@ access(all) contract ExampleMarketplace { } // cancelSale gives the owner the opportunity to cancel a sale in the collection - access(all) fun cancelSale(tokenID: UInt64) { + pub fun cancelSale(tokenID: UInt64) { // remove the price self.prices.remove(key: tokenID) self.prices[tokenID] = nil @@ -287,7 +287,7 @@ access(all) contract ExampleMarketplace { } // listForSale lists an NFT for sale in this collection - access(all) fun listForSale(tokenID: UInt64, price: UFix64) { + pub fun listForSale(tokenID: UInt64, price: UFix64) { pre { self.ownerCollection.borrow()!.idExists(id: tokenID): "NFT to be listed does not exist in the owner's collection" @@ -299,14 +299,14 @@ access(all) contract ExampleMarketplace { } // changePrice changes the price of a token that is currently for sale - access(all) fun changePrice(tokenID: UInt64, newPrice: UFix64) { + pub fun changePrice(tokenID: UInt64, newPrice: UFix64) { self.prices[tokenID] = newPrice emit PriceChanged(id: tokenID, newPrice: newPrice, owner: self.owner?.address) } // purchase lets a user send tokens to purchase an NFT that is for sale - access(all) fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) { + pub fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) { pre { self.prices[tokenID] != nil: "No token matching this ID for sale!" @@ -338,18 +338,18 @@ access(all) contract ExampleMarketplace { } // idPrice returns the price of a specific token in the sale - access(all) fun idPrice(tokenID: UInt64): UFix64? { + pub fun idPrice(tokenID: UInt64): UFix64? { return self.prices[tokenID] } // getIDs returns an array of token IDs that are for sale - access(all) fun getIDs(): [UInt64] { + pub fun getIDs(): [UInt64] { return self.prices.keys } } // createCollection returns a new collection resource to the caller - access(all) fun createSaleCollection(ownerCollection: Capability<&ExampleNFT.Collection>, + pub fun createSaleCollection(ownerCollection: Capability<&ExampleNFT.Collection>, ownerVault: Capability<&AnyResource{ExampleToken.Receiver}>): @SaleCollection { return <- create SaleCollection(ownerCollection: ownerCollection, ownerVault: ownerVault) } @@ -365,7 +365,7 @@ that was explained in [Non-Fungible Tokens](./05-non-fungible-tokens-1.md), with Then, another user can call the `purchase` method, sending their `ExampleToken.Vault` that contains the currency they are using to make the purchase. The buyer also includes a capability to their NFT `ExampleNFT.Collection` so that the purchased token can be immediately deposited into their collection when the purchase is made. -- This marketplace contract stores a capability: `access(all) let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>`. +- This marketplace contract stores a capability: `pub let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>`. The owner of the sale saves a capability to their Fungible Token `Receiver` within the sale. This allows the sale resource to be able to immediately deposit the currency that was used to buy the NFT into the owners `Vault` when a purchase is made. @@ -374,16 +374,16 @@ that was explained in [Non-Fungible Tokens](./05-non-fungible-tokens-1.md), with ```cadence // Event that is emitted when a new NFT is put up for sale - access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) + pub event ForSale(id: UInt64, price: UFix64, owner: Address?) // Event that is emitted when the price of an NFT changes - access(all) event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) + pub event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) // Event that is emitted when a token is purchased - access(all) event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) + pub event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) // Event that is emitted when a seller withdraws their NFT from the sale - access(all) event SaleCanceled(id: UInt64, seller: Address?) + pub event SaleCanceled(id: UInt64, seller: Address?) ``` This contract has a few new features and concepts that are important to cover: @@ -401,7 +401,7 @@ when getting information about their users' accounts or generating analytics. Events are declared by indicating [the access level](../language/access-control), `event`, and the name and parameters of the event, like a function declaration: ```cadence -access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) +pub event ForSale(id: UInt64, price: UFix64, owner: Address?) ``` Events cannot modify state at all; they indicate when important actions happen in the smart contract. @@ -422,7 +422,7 @@ As you hopefully understand, [capabilites](../language/capabilities.md) are links to private objects in account storage that specify and expose a subset in the public or private namespace of public or private paths where the Capability is linked. -To create a capability, a user typically uses [the `AuthAccount.link`](../language/accounts) +To create a capability, a user typically uses [the `AuthAccount.link`](../language/accounts.mdx#authaccount) method to create a link to a resource in their private storage, specifying a type to link the capability as: ```cadence @@ -434,7 +434,7 @@ acct.link<&ExampleToken.Vault{ExampleToken.Receiver, ExampleToken.Balance}> (/public/CadenceFungibleTokenTutorialReceiver, target: /storage/CadenceFungibleTokenTutorialVault) ``` -Then, users can get that capability if it was created [in a public path](../language/accounts/paths), +Then, users can get that capability if it was created [in a public path](../language/accounts.mdx#paths), borrow it, and access the functionality that the owner specified. ```cadence @@ -489,7 +489,7 @@ One last piece to consider about capabilities is the decision about when to use This tutorial used to have the `SaleCollection` directly store the NFTs that were for sale, like so: ```cadence -access(all) resource SaleCollection: SalePublic { +pub resource SaleCollection: SalePublic { /// Dictionary of NFT objects for sale /// Maps ID to NFT resource object @@ -506,7 +506,7 @@ then those NFTs are not available to be shown to any app or smart contract that so it is as if the owner doesn't actually own the NFT! In cases like this, we usually recommend using a capability to the main collection so that the main collection can remain unchanged and fully usable by -other smart contracts and apps. This also means that if a for-sale NFT gets transferred by some means other than a purchase, then you need a way to get +other smart contracts and apps. This also means that if a for-sale NFT gets transferred by some means other than a purchase, then you need a way to get rid of the stale listing. That is out of the scope of this tutorial though. Enough explaining! Lets execute some code! @@ -554,7 +554,7 @@ transaction { sale.listForSale(tokenID: 1, price: 10.0) // Store the sale object in the account storage - acct.storage.save(<-sale, to: /storage/NFTSale) + acct.save(<-sale, to: /storage/NFTSale) // Create a public capability to the sale so that others can call its methods acct.link<&ExampleMarketplace.SaleCollection{ExampleMarketplace.SalePublic}>(/public/NFTSale, target: /storage/NFTSale) @@ -585,7 +585,7 @@ import ExampleNFT from 0x02 import ExampleMarketplace from 0x03 // This script prints the NFTs that account 0x01 has for sale. -access(all) fun main() { +pub fun main() { // Get the public account object for account 0x01 let account1 = getAccount(0x01) @@ -713,7 +713,7 @@ import ExampleMarketplace from 0x03 // // Account 1: Vault balance = 50, No NFTs // Account 2: Vault balance = 10, NFT ID=1 -access(all) fun main() { +pub fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) @@ -721,13 +721,13 @@ access(all) fun main() { // Get references to the account's receivers // by getting their public capability // and borrowing a reference from the capability - let acct1ReceiverRef = acct1.capabilities - .borrow<&ExampleToken.Vault{ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver) - ?? panic("Could not borrow acct1 vault reference") + let acct1ReceiverRef = acct1.getCapability(/public/CadenceFungibleTokenTutorialReceiver) + .borrow<&ExampleToken.Vault{ExampleToken.Balance}>() + ?? panic("Could not borrow acct1 vault reference") - let acct2ReceiverRef = acct2.capabilities - .borrow<&ExampleToken.Vault{ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver) - ?? panic("Could not borrow acct2 vault reference") + let acct2ReceiverRef = acct2.getCapability(/public/CadenceFungibleTokenTutorialReceiver) + .borrow<&ExampleToken.Vault{ExampleToken.Balance}>() + ?? panic("Could not borrow acct2 vault reference") // Log the Vault balance of both accounts and ensure they are // the correct numbers. @@ -806,14 +806,14 @@ If we wanted to build a central marketplace on-chain, we could use a contract th ```cadence CentralMarketplace.cdc // Marketplace would be the central contract where people can post their sale // references so that anyone can access them -access(all) contract Marketplace { +pub contract Marketplace { // Data structure to store active sales - access(all) var tokensForSale: {Address: Capability<&SaleCollection>)} + pub var tokensForSale: {Address: Capability<&SaleCollection>)} // listSaleCollection lists a users sale reference in the array // and returns the index of the sale so that users can know // how to remove it from the marketplace - access(all) fun listSaleCollection(collection: Capability<&SaleCollection>) { + pub fun listSaleCollection(collection: Capability<&SaleCollection>) { let saleRef = collection.borrow() ?? panic("Invalid sale collection capability") @@ -822,7 +822,7 @@ access(all) contract Marketplace { // removeSaleCollection removes a user's sale from the array // of sale references - access(all) fun removeSaleCollection(owner: Address) { + pub fun removeSaleCollection(owner: Address) { self.tokensForSale[owner] = nil } diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/09-voting.mdx b/docs/tutorial/09-voting.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/09-voting.mdx rename to docs/tutorial/09-voting.mdx diff --git a/docs/tutorial/10-resources-compose.md b/docs/tutorial/10-resources-compose.md index e2ddcf3..dd9ca95 100644 --- a/docs/tutorial/10-resources-compose.md +++ b/docs/tutorial/10-resources-compose.md @@ -8,7 +8,7 @@ In this tutorial, we're going to walk through how resources can own other resour Open the starter code for this tutorial in the Flow Playground: - @@ -92,12 +92,12 @@ The deployed contract should have the following contents: // support even more powerful versions of this. // -access(all) contract KittyVerse { +pub contract KittyVerse { // KittyHat is a special resource type that represents a hat - access(all) resource KittyHat { - access(all) let id: Int - access(all) let name: String + pub resource KittyHat { + pub let id: Int + pub let name: String init(id: Int, name: String) { self.id = id @@ -105,7 +105,7 @@ access(all) contract KittyVerse { } // An example of a function someone might put in their hat resource - access(all) fun tipHat(): String { + pub fun tipHat(): String { if self.name == "Cowboy Hat" { return "Howdy Y'all" } else if self.name == "Top Hat" { @@ -117,35 +117,35 @@ access(all) contract KittyVerse { } // Create a new hat - access(all) fun createHat(id: Int, name: String): @KittyHat { + pub fun createHat(id: Int, name: String): @KittyHat { return <-create KittyHat(id: id, name: name) } - access(all) resource Kitty { + pub resource Kitty { - access(all) let id: Int + pub let id: Int // place where the Kitty hats are stored - access(all) var items: @{String: KittyHat} + pub var items: @{String: KittyHat} init(newID: Int) { self.id = newID self.items <- {} } - access(all) fun getKittyItems(): @{String: KittyHat} { + pub fun getKittyItems(): @{String: KittyHat} { var other: @{String:KittyHat} <- {} self.items <-> other return <- other } - access(all) fun setKittyItems(items: @{String: KittyHat}) { + pub fun setKittyItems(items: @{String: KittyHat}) { var other <- items self.items <-> other destroy other } - access(all) fun removeKittyItem(key: String): @KittyHat? { + pub fun removeKittyItem(key: String): @KittyHat? { var removed <- self.items.remove(key: key) return <- removed } @@ -155,7 +155,7 @@ access(all) contract KittyVerse { } } - access(all) fun createKitty(): @Kitty { + pub fun createKitty(): @Kitty { return <-create Kitty(newID: 1) } @@ -169,7 +169,7 @@ The hats are stored in a variable in the Kitty resource. ```cadence // place where the Kitty hats are stored - access(all) var items: <-{String: KittyHat} + pub var items: <-{String: KittyHat} ``` A Kitty owner can take the hats off the Kitty and transfer them individually. Or the owner can transfer a Kitty that owns a hat, and the hat will go along with the Kitty. @@ -210,7 +210,7 @@ transaction { log("The cat has the hats") // Store the Kitty in storage - acct.storage.save(<-kitty, to: /storage/kitty) + acct.save(<-kitty, to: /storage/kitty) } } ``` @@ -243,7 +243,7 @@ transaction { prepare(acct: AuthAccount) { // Move the Kitty out of storage, which also moves its hat along with it - let kitty <- acct.storage.load<@KittyVerse.Kitty>(from: /storage/kitty) + let kitty <- acct.load<@KittyVerse.Kitty>(from: /storage/kitty) ?? panic("Kitty doesn't exist!") // Take the cowboy hat off the Kitty @@ -259,7 +259,7 @@ transaction { // Move the Kitty to storage, which // also moves its hat along with it. - acct.storage.save(<-kitty, to: /storage/kitty) + acct.save(<-kitty, to: /storage/kitty) } } ``` diff --git a/docusaurus.config.js b/docusaurus.config.js index 13cf8f9..37def1b 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -41,13 +41,13 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - lastVersion: 'current_0.42', + lastVersion: 'current', versions: { current: { - label: '1.0', - }, - 'current_0.42': { label: '0.42', + }, + '1.0': { + label: '1.0', } }, beforeDefaultRemarkPlugins: [ @@ -104,7 +104,7 @@ const config = { }, { label: 'Solidity Guide', - to: '/docs/0.42/solidity-to-cadence', + to: '/docs/solidity-to-cadence', position: 'right', }, { diff --git a/versioned_docs/version-1.0/anti-patterns.md b/versioned_docs/version-1.0/anti-patterns.md new file mode 100644 index 0000000..6acc46f --- /dev/null +++ b/versioned_docs/version-1.0/anti-patterns.md @@ -0,0 +1,250 @@ +--- +title: Cadence Anti-Patterns +sidebar_position: 6 +sidebar_label: Anti-Patterns +--- + +This is an opinionated list of issues that can be improved if they are found in Cadence code intended for production. + +## Avoid using authorized account references as a function parameter + +### Problem + +A developer may choose to authenticate or perform operations for their users by using the users' account addresses. +In order to do this, they might add a parameter to a function which has an authorized account reference type (`auth(...) &Account`), +as an authorized account reference can only be obtained by signing a transaction. + +This is problematic, as the authorized account reference allows access to some sensitive operations on the account, +for example, to write to storage, +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 a `auth(Storage) &Account` parameter +// to authenticate users to transfer NFTs + +// They could deploy the contract with an Ethereum-style access control list functionality + +access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { + 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 + +access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { + 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 authorized account reference (`auth(...) &Account`) is necessary, +such as a cold storage multi-sig, +but those cases are rare and such usage should still be avoided unless absolutely necessary. + +## 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 require access through an [entitlement](./language/access-control.md#entitlements) (`access(E)`), +or use a non-public [access modifier](./language/access-control.md) like `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. + +## 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 initializer, +and then a new creation function can be potentially included within the admin resource, if necessary. + +### Example + +```cadence +// Pseudo-code + +// BAD +access(all) contract Currency { + access(all) resource Admin { + access(all) fun mintTokens() + } + + // Anyone in the network can call this function + // And use the Admin resource to mint tokens + access(all) 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 +access(all) contract Currency { + access(all) resource Admin { + access(all) fun mintTokens() + + // Only an admin can create new Admins + access(all) fun createAdmin(): @Admin { + return <-create Admin() + } + } + + init() { + // Create a single admin resource + let firstAdmin <- create Admin() + + // Store it in private account storage, so only the admin can use it + self.account.storage.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 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 +// +access(all) contract TopShot { + + // The Record that is used to track every unique play ID + access(all) var nextPlayID: UInt32 + + access(all) struct Play { + + access(all) 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 +// +access(all) contract TopShot { + + // The Record that is used to track every unique play ID + access(all) var nextPlayID: UInt32 + + access(all) struct Play { + + access(all) let playID: UInt32 + + init() { + self.playID = TopShot.nextPlayID + } + } + + access(all) resource Admin { + + // Protected within the private admin resource + access(all) 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/versioned_docs/version-current_0.42/0.42/contract-upgrades.md b/versioned_docs/version-1.0/contract-upgrades.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/contract-upgrades.md rename to versioned_docs/version-1.0/contract-upgrades.md diff --git a/versioned_docs/version-current_0.42/0.42/design-patterns.md b/versioned_docs/version-1.0/design-patterns.md similarity index 62% rename from versioned_docs/version-current_0.42/0.42/design-patterns.md rename to versioned_docs/version-1.0/design-patterns.md index 24f6796..186b5d2 100644 --- a/versioned_docs/version-current_0.42/0.42/design-patterns.md +++ b/versioned_docs/version-1.0/design-patterns.md @@ -29,7 +29,7 @@ See [Wikipedia's page on magic numbers](https://en.wikipedia.org/wiki/Magic_numb ### Solution -Add a public (`pub`), constant (`let`) field, e.g. a `Path` , to the contract responsible for the value, +Add a public (`access(all)`), 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. @@ -38,27 +38,27 @@ Example Snippet: ```cadence // BAD Practice: Do not hard code storage paths -pub contract NamedFields { - pub resource Test {} +access(all) contract NamedFields { + access(all) resource Test {} init() { // BAD: Hard-coded storage path - self.account.save(<-create Test(), to: /storage/testStorage) + self.account.storage.save(<-create Test(), to: /storage/testStorage) } } // GOOD practice: Instead, use a field // -pub contract NamedFields { - pub resource Test {} +access(all) contract NamedFields { + access(all) resource Test {} // GOOD: field storage path - pub let TestStoragePath: StoragePath + access(all) 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) + self.account.storage.save(<-create Test(), to: self.TestStoragePath) } } @@ -74,14 +74,13 @@ 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. +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`). +Make sure that the field can be accessed from a script. 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. +Making the field or function `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. @@ -92,7 +91,7 @@ Example: access(self) let totalSupply: UFix64 // GOOD: Field is public, so it can be read and used by anyone -pub let totalSupply: UFix64 +access(all) let totalSupply: UFix64 ``` ## Script-Accessible report @@ -115,12 +114,12 @@ 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 +### Example ```cadence -pub contract AContract { - pub let BResourceStoragePath: StoragePath - pub let BResourcePublicPath: PublicPath +access(all) contract AContract { + access(all) let BResourceStoragePath: StoragePath + access(all) let BResourcePublicPath: PublicPath init() { self.BResourceStoragePath = /storage/BResource @@ -128,15 +127,15 @@ pub contract AContract { } // Resource definition - pub resource BResource { - pub var c: UInt64 - pub var d: String + access(all) resource BResource { + access(all) var c: UInt64 + access(all) 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 { + access(all) fun generateReport(): BReportStruct { return BReportStruct(c: self.c, d: self.d) } @@ -147,9 +146,9 @@ pub contract AContract { } // Define a struct with the same fields as the resource - pub struct BReportStruct { - pub var c: UInt64 - pub var d: String + access(all) struct BReportStruct { + access(all) var c: UInt64 + access(all) var d: String init(c: UInt64, d: String) { self.c = c @@ -163,26 +162,26 @@ pub contract AContract { import AContract from 0xAContract transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(IssueStorageCapabilityController, PublishCapability) &Account) { //... - acct.link<&AContract.BResource>(AContract.BResourcePublicPath, target: AContract.BResourceStoragePath) + let cap = acct.capabilities.storage.issue<&AContract.BResource>(AContract.BResourceStoragePath) + acct.capabilities.publish(cap, at: AContract.BResourcePublicPath) } } // Script import AContract from 0xAContract // Return the struct with a script -pub fun main(account: Address): AContract.BReportStruct { +access(all) fun main(account: Address): AContract.BReportStruct { // borrow the resource - let b = getAccount(account) - .getCapability(AContract.BResourcePublicPath) - .borrow<&AContract.BResource>() + let b = getAccount(account).capabilities + .borrow<&AContract.BResource>(AContract.BResourcePublicPath) // return the struct return b.generateReport() } ``` -## Init Singleton +## Init singleton ### Problem @@ -191,10 +190,10 @@ There should not be a function to do this, as that would allow anyone to create ### 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 initializer +and deliver them to an address or `&Account` specified as an argument. -See how this is done in the LockedTokens contract init function: +See how this is done in the LockedTokens contract initializer: [LockedTokens.cdc](https://github.com/onflow/flow-core-contracts/blob/master/contracts/LockedTokens.cdc#L718) @@ -222,15 +221,16 @@ All fields, functions, types, variables, etc., need to have names that clearly d `/storage/bestPracticesDocsCollectionPath` is better than `/storage/collection` ### Example + ```cadence // BAD: Unclear naming // -pub contract Tax { +access(all) contract Tax { // Do not use abbreviations unless absolutely necessary - pub var pcnt: UFix64 + access(all) var pcnt: UFix64 // Not clear what the function is calculating or what the parameter should be - pub fun calculate(num: UFix64): UFix64 { + access(all) fun calculate(num: UFix64): UFix64 { // What total is this referring to? let total = num + (num * self.pcnt) @@ -240,13 +240,13 @@ pub contract Tax { // GOOD: Clear naming // -pub contract TaxUtilities { +access(all) contract TaxUtilities { // Clearly states what the field is for - pub var taxPercentage: UFix64 + access(all) var taxPercentage: UFix64 // Clearly states that this function calculates the // total cost after tax - pub fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { + access(all) fun calculateTotalCostPlusTax(preTaxCost: UFix64): UFix64 { let postTaxCost = preTaxCost + (preTaxCost * self.taxPercentage) return postTaxCost @@ -254,31 +254,9 @@ pub contract TaxUtilities { } ``` -## 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. +For example, use `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. @@ -299,17 +277,17 @@ It is usually safe to include post-conditions in transactions to verify the inte This could be used when purchasing an NFT to verify that the NFT was deposited in your account's collection. ```cadence -// Psuedo-code +// Pseudo-code transaction { - pub let buyerCollectionRef: &NonFungibleToken.Collection + access(all) let buyerCollectionRef: &NonFungibleToken.Collection - prepare(acct: AuthAccount) { + prepare(acct: auth(BorrowValue) &Account) { // 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) + let self.buyerCollectionRef = acct.storage.borrow(from: /storage/Collection) // purchase, supplying the buyers collection reference saleRef.purchase(tokenID: 1, recipient: self.buyerCollectionRef, buyTokens: <-temporaryVault) @@ -322,20 +300,23 @@ transaction { } ``` -## Avoid excessive load and save storage operations (prefer in-place mutations) +## Avoid unnecessary 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. +When modifying data in account storage, `load()` and `save()` are costly operations: +All data is unnecessarily moved out of the account, then moved back into the account. +This can quickly cause your transaction to reach its limits. -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. +This also applies to nested, stored in fields, arrays, and dictionaries: +Moving objects out of containers and moving them back into the container, +just to modify the object, 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. +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: +the dictionary can be updated in-place, which is easier and more efficient. 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. @@ -346,6 +327,9 @@ For making modifications to values in storage or accessing stored objects, `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. +Fields and value in containers, such as in arrays and dictionaries, +can be borrowed using a reference expression (`&v as &T`). + ### Example ```cadence @@ -353,10 +337,10 @@ This reference can be assigned to or can be used to access fields or call method // transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(LoadValue, SaveValue) &Account) { // Removes the vault from storage, a costly operation - let vault <- acct.load<@ExampleToken.Vault>(from: /storage/exampleToken) + let vault <- acct.storage.load<@ExampleToken.Vault>(from: /storage/exampleToken) // Withdraws tokens let burnVault <- vault.withdraw(amount: 10) @@ -364,7 +348,7 @@ transaction { destroy burnVault // Saves the used vault back to storage, another costly operation - acct.save(to: /storage/exampleToken) + acct.storage.save(to: /storage/exampleToken) } } @@ -373,10 +357,10 @@ transaction { // transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(BorrowValue) &Account) { // Borrows a reference to the stored vault, much less costly operation - let vault <- acct.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) + let vault <- acct.storage.borrow<&ExampleToken.Vault>(from: /storage/exampleToken) let burnVault <- vault.withdraw(amount: 10) @@ -389,13 +373,12 @@ transaction { # Capabilities -## Capability Bootstrapping +## Capability bootstrapping ### Problem -An account must be given a [capability](./language/capabilities.md) -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. +An account must be given a [capability](./language/capabilities.md) to an object stored in another account. +To create (issue) 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 @@ -406,7 +389,7 @@ from one account and delivering it to the other. ### Solution -The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](./language/accounts.mdx#account-inbox) +The solution to the bootstrapping problem in Cadence is provided by the [Inbox API](./language/accounts/inbox.mdx). 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. @@ -426,67 +409,50 @@ and emits an `InboxValueUnpublished` event that the recipient can listen for off 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 +or limit the type with which the capability is authorized in order to only give recipients access to the functionality that the provider is willing to allow them to copy. -## Capability Revocation + +## 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` +The first account should issue a _new_ capability +and use it only for the purpose of granting the second account access. -`/private/revokableLink` -> `/private/capability` +Once the first account wants to revoke access to the resource in storage, +they can simply get the capability controller for that capability and delete it. -**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. +## Check for existing capability before publishing new one -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. +### Problem +When publishing a capability, a capability might be already be published at the specified path. -## Check for existing links before creating new ones +### Solution -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. +Check if a capability is already published at the given path. ### 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) - } + prepare(signer: auth(Capabilities) &Account) { + let capability = signer.capabilities.storage + .issue<&ExampleToken.Vault>(/storage/exampleTokenVault) - signer.link<&ExampleToken.Vault{FungibleToken.Receiver}>( - /public/exampleTokenReceiver, - target: /storage/exampleTokenVault - ) + let publicPath = /public/exampleTokenReceiver + + if signer.capabilities.exits(publicPath) { + signer.capabilities.unpublish(publicPath) + } + signer.capabilities.publish(capability, at: publicPath) } } ``` diff --git a/versioned_docs/version-current_0.42/0.42/index.md b/versioned_docs/version-1.0/index.md similarity index 99% rename from versioned_docs/version-current_0.42/0.42/index.md rename to versioned_docs/version-1.0/index.md index 144eec7..badebc2 100644 --- a/versioned_docs/version-current_0.42/0.42/index.md +++ b/versioned_docs/version-1.0/index.md @@ -3,7 +3,7 @@ title: Introduction to Cadence sidebar_position: 1 sidebar_label: Introduction --- - + In a blockchain environment like Flow, programs that are stored on-chain in accounts are commonly referred to as smart contracts. A smart contract is a program that verifies and executes the performance of a contract without the need for a trusted third party. Programs that run on blockchains are commonly referred to as smart contracts because they mediate important functionality (such as currency) diff --git a/versioned_docs/version-current_0.42/0.42/json-cadence-spec.md b/versioned_docs/version-1.0/json-cadence-spec.md similarity index 99% rename from versioned_docs/version-current_0.42/0.42/json-cadence-spec.md rename to versioned_docs/version-1.0/json-cadence-spec.md index eaaa1c7..5e059bb 100644 --- a/versioned_docs/version-current_0.42/0.42/json-cadence-spec.md +++ b/versioned_docs/version-1.0/json-cadence-spec.md @@ -403,7 +403,7 @@ Function values can only be exported, they cannot be imported. "value": { "functionType": { "kind": "Function", - "typeID": "(():Void)", + "typeID": "fun():Void", "parameters": [], "return": { "kind": "Void" @@ -729,14 +729,13 @@ Initializer types are encoded a list of parameters to the initializer. --- -## Restricted Types +## Intersection Types ```json { - "kind": "Restriction", + "kind": "Intersection", "typeID": "", - "type": , - "restrictions": [ + "types": [ , , //... diff --git a/versioned_docs/version-current_0.42/0.42/language/_category_.json b/versioned_docs/version-1.0/language/_category_.json similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/_category_.json rename to versioned_docs/version-1.0/language/_category_.json diff --git a/versioned_docs/version-1.0/language/access-control.md b/versioned_docs/version-1.0/language/access-control.md new file mode 100644 index 0000000..8a020d3 --- /dev/null +++ b/versioned_docs/version-1.0/language/access-control.md @@ -0,0 +1,610 @@ +--- +title: Access control +sidebar_position: 13 +--- + +Access control allows making certain parts of a program accessible/visible +and making other parts inaccessible/invisible. + +In Cadence, access control consists of: + +1. Access control on objects in account storage, + using [capability security](./capabilities.md). + + A user is not able to access an object + unless they own the object or have a reference to that object. + This means that nothing is truly public by default. + + Other accounts can not read or write the objects in an account + unless the owner of the account has granted them access + by providing references to the objects. + + This kind of access control is covered in the pages + on [capabilities](./capabilities.md) + and [capability management](./accounts/capabilities.mdx). + +2. Access control within contracts and objects, + using access modifiers (`access` keyword). + +This page covers the second part of access control, +using access modifiers. + +All declarations, such as [function](./functions.mdx), [composite types](./composite-types.mdx), and fields, +must be prefixed with an access modifier, using the `access` keyword. + +The access modifier determines where the declaration is accessible/visible. +Fields can only be assigned to and mutated from within the same or inner scope. + +For example, to make a function publicly accessible (`access(all)` is explained below): + +``` +access(all) fun test() {} +``` + +There are five levels of access control: + +- **Public access** means the declaration is accessible/visible in all scopes. + This includes the current scope, inner scopes, and the outer scopes. + + A declaration is made publicly accessible using the `access(all)` modifier. + + For example, a public field in a type can be accessed + on an instance of the type in an outer scope. + +- **Entitled access** means the declaration is only accessible/visible + to the owner of the object, or to [references](./references.mdx) + that are authorized to the required [entitlements](#entitlements). + + A declaration is made accessible through entitlements by using the `access(E)` syntax, + where `E` is a set of one or more entitlements, + or a single [entitlement mapping](#entitlement-mappings). + + A reference is considered authorized to an entitlement + if that entitlement appears in the `auth` portion of the reference type. + + For example, an `access(E, F)` field on a resource `R` can only be accessed by an owned (`@R`-typed) value, + or a reference to `R` that is authorized to the `E` and `F` entitlements (`auth(E, F) &R`). + +- **Account access** means the declaration is only accessible/visible + in the scope of the entire account where it is defined. + This means that other contracts in the account are able to access it. + + A declaration is made accessible by code in the same account, + for example other contracts, by using the `access(account)` keyword. + +- **Contract access** means the declaration is only accessible/visible + in the scope of the contract that defined it. + This means that other declarations that are defined in the same contract can access it, + but not other contracts in the same account. + + A declaration is made accessible by code in the same contract + by using the `access(contract)` keyword. + +- **Private access** means the declaration is only accessible/visible + in the current and inner scopes. + + A declaration is made accessible by code in the same containing type + by using the `access(self)` keyword. + + For example, an `access(self)` field can only be accessed + by functions of the type is part of, not by code in an outer scope. + +To summarize the behavior for variable declarations, constant declarations, and fields: + +| Declaration kind | Access modifier | Accessible in | Assignable in | Mutable in | +|:-----------------|:-------------------|:-----------------------------------------------------|:------------------|:------------------| +| `let` | `access(self)` | Current and inner | *None* | Current and inner | +| `let` | `access(contract)` | Current, inner, and containing contract | *None* | Current and inner | +| `let` | `access(account)` | Current, inner, and other contracts in same account | *None* | Current and inner | +| `let` | `access(all)` | **All** | *None* | Current and inner | +| `let` | `access(E)` | **All** with required entitlements | *None* | Current and inner | +| `var` | `access(self)` | Current and inner | Current and inner | Current and inner | +| `var` | `access(contract)` | Current, inner, and containing contract | Current and inner | Current and inner | +| `var` | `access(account)` | Current, inner, and other contracts in same account | Current and inner | Current and inner | +| `var` | `access(all)` | **All** | Current and inner | Current and inner | +| `var` | `access(E)` | **All** with required entitlements | Current and inner | Current and inner | + +Declarations of [composite types](./composite-types.mdx) must be public. +However, even though the declarations/types are publicly visible, +resources can only be created, and events can only be emitted +from inside the contract they are declared in. + +```cadence +// Declare a private constant, inaccessible/invisible in outer scope. +// +access(self) let a = 1 + +// Declare a public constant, accessible/visible in all scopes. +// +access(all) let b = 2 +``` + +```cadence +// Declare a public struct, accessible/visible in all scopes. +// +access(all) struct SomeStruct { + + // Declare a private constant field which is only readable + // in the current and inner scopes. + // + access(self) let a: Int + + // Declare a public constant field which is readable in all scopes. + // + access(all) let b: Int + + // Declare a private variable field which is only readable + // and writable in the current and inner scopes. + // + access(self) var c: Int + + // Declare a public variable field which is not settable, + // so it is only writable in the current and inner scopes, + // and readable in all scopes. + // + access(all) var d: Int + + // Arrays and dictionaries declared without (set) cannot be + // mutated in external scopes + access(all) let arr: [Int] + + // The initializer is omitted for brevity. + + // Declare a private function which is only callable + // in the current and inner scopes. + // + access(self) fun privateTest() { + // ... + } + + // Declare a public function which is callable in all scopes. + // + access(all) fun publicTest() { + // ... + } + + // The initializer is omitted for brevity. + +} + +let some = SomeStruct() + +// Invalid: cannot read private constant field in outer scope. +// +some.a + +// Invalid: cannot set private constant field in outer scope. +// +some.a = 1 + +// Valid: can read public constant field in outer scope. +// +some.b + +// Invalid: cannot set public constant field in outer scope. +// +some.b = 2 + +// Invalid: cannot read private variable field in outer scope. +// +some.c + +// Invalid: cannot set private variable field in outer scope. +// +some.c = 3 + +// Valid: can read public variable field in outer scope. +// +some.d + +// Invalid: cannot set public variable field in outer scope. +// +some.d = 4 + +// Invalid: cannot mutate a public field in outer scope. +// +some.f.append(0) + +// Invalid: cannot mutate a public field in outer scope. +// +some.f[3] = 1 + +// Valid: can call non-mutating methods on a public field in outer scope +some.f.contains(0) +``` + +## Entitlements + +Entitlements provide granular access control to each member of a composite. +Entitlements are declared using the syntax `entitlement E`, +where `E` is the name of the entitlement. + +For example, the following code declares two entitlements called `E` and `F`: + +```cadence +entitlement E +entitlement F +``` + +Entitlements can be imported from other contracts and used the same way as other types. +When using entitlements defined in another contract, the same qualified name syntax is used as for other types: + +```cadence +contract C { + entitlement E +} +``` + +Outside of `C`, `E` is used with `C.E` syntax. + +Entitlements exist in the same namespace as types, so if a contract declares a resource called `R`, +it is impossible to declare an entitlement that is also called `R`. + +Entitlements can be used in access modifiers composite members (fields and functions) +to specify which references to those composites are allowed to access those members. + +An access modifier can include more than one entitlement, +joined with either an `|`, to indicate disjunction ("or"), +or a `,`, to indicate conjunction ("and"). +The two kinds of separators cannot be combined in the same set. + +For example: + +```cadence +access(all) +resource SomeResource { + + // requires a reference to have an `E` entitlement to read this field + access(E) + let a: Int + + // requires a reference to have either an `E` OR an `F` entitlement to read this field. + access(E | F) + let b: Int + + // requires a reference to have both an `E` AND an `F` entitlement to read this field + access(E, F) + let b: Int + + // intializers omitted for brevity + // ... +} +``` + +Assuming the following constants exists, +which have owned or [reference](./references.mdx) types: + +```cadence +let r: @SomeResource = // ... +let refE: auth(E) &SomeResource = // ... +let refF: auth(F) &SomeResource = // ... +let refEF: auth(E, F) &SomeResource = // ... +``` + +The references can be used as follows: + +```cadence +// valid, because `r` is owned and thus is "fully entitled" +r.a +// valid, because `r` is owned and thus is "fully entitled" +r.b +// valid, because `r` is owned and thus is "fully entitled" +r.c + +// valid, because `refE` has an `E` entitlement as required +refE.a +// valid, because `refE` has one of the two required entitlements +refE.b +// invalid, because `refE` only has one of the two required entitlements +refE.c + +// invalid, because `refF` has an `E` entitlement, not an `F` +refF.a +// valid, because `refF` has one of the two required entitlements +refF.b +// invalid, because `refF` only has one of the two required entitlements +refF.c + +// valid, because `refEF` has an `E` entitlement +refEF.a +// valid, because `refEF` has both of the two required entitlements +refEF.b +// valid, because `refEF` has both of the two required entitlements +refEF.c +``` + +Note particularly in this example how the owned value `r` can access all entitled members on `SomeResource`. +Owned values are not affected by entitled declarations. + +### Entitlement mappings + +Entitlement mappings are a way to statically declare how entitlements are propagated +from parents to child objects in a nesting hierarchy. + +When objects have fields that are child objects, +to grant access to the inner object based on the entitlements of the reference to the parent object. + +Consider the following example, +which uses entitlements to control access to an inner resource: + +```cadence +entitlement OuterEntitlement +entitlement InnerEntitlement + +resource InnerResource { + access(all) + fun foo() { ... } + + access(InnerEntitlement) + fun bar() { ... } +} + +resource OuterResource { + access(self) + let childResource: @InnerResource + + init(childResource: @InnerResource) { + self.childResource <- childResource + } + + // The parent resource has to provide two accessor functions + // which return a reference to the inner resource. + // + // If the reference to the outer resource is unauthorized + // and does not have the OuterEntitlement entitlement, + // the outer resource allows getting an unauthorized reference + // to the inner resource. + // + // If the reference to the outer resource is authorized + // and it has the OuterEntitlement entitlement, + // the outer resource allows getting an authorized reference + // to the inner resource. + + access(all) + fun getPubRef(): &InnerResource { + return &self.childResource as &InnerResource + } + + access(OuterEntitlement) + fun getEntitledRef(): auth(InnerEntitlement) &InnerResource { + return &self.childResource as auth(InnerEntitlement) &InnerResource + } +} +``` + +With this pattern, it is possible to store a `InnerResource` in an `OuterResource`, +and create different ways to access that nested resource depending on the entitlement one possesses. + +An unauthorized reference to `OuterResource` can only be used to call the `getPubRef` function, +and thus can only obtain an unauthorized reference to `InnerResource`. +That reference to the `InnerResource` then only allows calling the function `foo`, which is publicly accessible, +but not function `bar`, as it needs the `InnerEntitlement` entitlement, which is not granted. + +However a `OuterEntitlement`-authorized reference to the `OuterResource` can be used to call the `getEntitledRef` function, +which returns a `InnerEntitlement`-authorized reference to `InnerResource`, +which in turn can be used to call function `bar`. + +This pattern is functional, but it is unfortunate that the accessor functions to `InnerResource` have to be "duplicated". + +To avoid necessitating this duplication, entitlement mappings can be used. + +Entitlement mappings are declared using the syntax: + +```cadence +entitlement mapping M { + // ... +} +``` + +Where `M` is the name of the mapping. + +The body of the mapping contains zero or more rules of the form `A -> B`, +where `A` and `B` are entitlements. +Each rule defines that, given a reference with the entitlement on the left, +a reference with the entitlement on the right is returned. + +An entitlement mapping thus defines a function from an input set of entitlements (called the domain) +to an output set (called the range or the image). + +Using entitlement mappings, the above example could be equivalently written as: + +```cadence +entitlement OuterEntitlement +entitlement InnerEntitlement + +// Specify a mapping for entitlements called `OuterToInnerMap`, +// which maps the entitlement `OuterEntitlement` to the entitlement `InnerEntitlement`. +entitlement mapping OuterToInnerMap { + OuterEntitlement -> InnerEntitlement +} + +resource InnerResource { + access(all) + fun foo() { ... } + + access(InnerEntitlement) + fun bar() { ... } +} + +resource OuterResource { + // Use the entitlement mapping `OuterToInnerMap`. + // + // This declares that when the field `childResource` is accessed + // using a reference authorized with the entitlement `OuterEntitlement`, + // then a reference with the entitlement `InnerEntitlement` is returned. + // + // This is equivalent to the two accessor functions + // that were necessary in the previous example. + // + access(OuterToInnerMap) + let childResource: @InnerResource + + init(childResource: @InnerResource) { + self.childResource <- childResource + } + + // No accessor functions are needed. +} + +// given some value `r` of type `@OuterResource` + +let pubRef = &r as &OuterResource +let pubInnerRef = pubRef.childResource // has type `&InnerResource` + +let entitledRef = &r as auth(OuterEntitlement) &OuterResource +let entitledInnerRef = entitledRef.childResource // has type `auth(InnerEntitlement) &InnerResource`, + // as `OuterEntitlement` is defined to map to `InnerEntitlement`. + +// `r` is an owned value, and is thus considered "fully-entitled" to `OuterResource`, +// so this access yields a value authorized to the entire image of `OuterToInnerMap`, +// in this case `InnerEntitlement` +let alsoEntitledInnerRef = r.childResource +``` + +Entitlement mappings can be used either in accessor functions (as in the example above), +or in fields whose types are either references, or containers (composite types, dictionaries, and arrays). + +Entitlement mappings need not be 1:1. +It is valid to define a mapping where many inputs map to the same output, +or where one input maps to many outputs. + +Entitlement mappings preserve the "kind" of the set they are mapping. +That is, mapping a conjunction ("and") set produces a conjunction set, +and mapping a disjunction ("or") set produces a disjunction set. + +Because entitlement separators cannot be combined in the same set, +attempting to map disjunction ("or") sets through certain complex mappings can result in a type error. + +For example, given the following entitlement mapping: + +```cadence +entitlement mapping M { + A -> B + A -> C + D -> E +} +``` + +Attempting to map `(A | D)` through `M` will fail, +since `A` should map to `(B, C)` and `D` should map to `E`, +but these two outputs cannot be combined into a disjunction ("or") set. + +A good example for how entitlement mappings can be used is the [`Account` type](./accounts/index.mdx). + +### The `Identity` entitlement mapping + +`Identity` is a built-in entitlement mapping that maps every input to itself as the output. +Any entitlement set passed through the `Identity` map will come out unchanged in the output. + +For instance: + +```cadence +entitlement X + +resource InnerResource { + // ... +} + +resource OuterResource { + access(Identity) + let childResource: @InnerResource + + access(Identity) + getChildResource(): auth(Identity) &InnerResource { + return &self.childResource + } + + init(childResource: @InnerResource) { + self.childResource <- childResource + } +} + +fun example(outerRef: auth(X) &OuterResource) { + let innerRef = outerRef.childResource // `innerRef` has type `auth(X) &InnerResource`, + // as `outerRef` was authorized with entitlement `X` +} +``` + +One important point to note about the `Identity` mapping, however, +is that its full output range is unknown, and theoretically infinite. +Because of that, +accessing an `Identity`-mapped field or function with an owned value will yield an empty output set. + +For example, calling `getChildResource()` on an owned `OuterResource` value, +will produce an unauthorized `&InnerResource` reference. + +### Mapping composition + +Entitlement mappings can be composed. +In the definition of an entitlement mapping, +it is possible to include the definition of one or more other mappings, +to copy over their mapping relations. + +An entitlement mapping is included into another entitlement mapping using the `include M` syntax, +where `M` is the name of the entitlement mapping to be included. + +In general, an `include M` statement in the definition of an entitlement mapping `N` +is equivalent to simply copy-pasting all the relations defined in `M` into `N`'s definition. + +Support for `include` is provided primarily to reduce code-reuse and promote composition. + +For example: + +```cadence +entitlement mapping M { + X -> Y + Y -> Z +} + +entitlement mapping N { + E -> F +} + +entitlement mapping P { + include M + include N + F -> G +} +``` + +The entitlement mapping `P` includes all of the relations defined in `M` and `N`, +along with the additional relations defined in its own definition. + +It is also possible to include the `Identity` mapping. +Any mapping `M` that includes the `Identity` mapping will map its input set to itself, +along with any additional relations defined in the mapping, +or in other included mappings. + +For instance: + +```cadence +entitlement mapping M { + include Identity + X -> Y +} +``` + +The mapping `M` maps the entitlement set `(X)` to `(X, Y)`, +and `(Y)` to `(Y)`. + +Includes that produce a cyclical mapping are rejected by the type-checker. + +### Built-in mutability entitlements + +A prominent use-case of entitlements is to control access to object based on mutability. + +For example, in a composite, the author would want to control the access to certain fields to be read-only, +and some fields to be mutable, etc. + +In order to support this, the following built-in entitlements can be used: +- `Insert` +- `Remove` +- `Mutate` + +These are primarily used by the built-in [array](./values-and-types.mdx#arrays) +and [dictionary](./values-and-types.mdx#dictionaries) functions, +but are available to be used in access modifiers of any declaration. + +While Cadence does not support entitlement composition or inheritance, +the `Mutate` entitlement is intended to be used as an equivalent form +to the conjunction of the `(Insert, Remove)` entitlement set. diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/_category_.json b/versioned_docs/version-1.0/language/accounts/_category_.json similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/_category_.json rename to versioned_docs/version-1.0/language/accounts/_category_.json diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/capabilities.mdx b/versioned_docs/version-1.0/language/accounts/capabilities.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/capabilities.mdx rename to versioned_docs/version-1.0/language/accounts/capabilities.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/contracts.mdx b/versioned_docs/version-1.0/language/accounts/contracts.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/contracts.mdx rename to versioned_docs/version-1.0/language/accounts/contracts.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/inbox.mdx b/versioned_docs/version-1.0/language/accounts/inbox.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/inbox.mdx rename to versioned_docs/version-1.0/language/accounts/inbox.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/index.mdx b/versioned_docs/version-1.0/language/accounts/index.mdx similarity index 98% rename from versioned_docs/version-current_0.42/0.42/language/accounts/index.mdx rename to versioned_docs/version-1.0/language/accounts/index.mdx index c85e77b..337fee6 100644 --- a/versioned_docs/version-current_0.42/0.42/language/accounts/index.mdx +++ b/versioned_docs/version-1.0/language/accounts/index.mdx @@ -3,8 +3,8 @@ title: Accounts --- The type `Account` provides access to accounts, -Accounts are only accessed through [references](../references.md), -which might be [authorized](../references.md#authorized-references). +Accounts are only accessed through [references](../references.mdx), +which might be [authorized](../references.mdx#authorized-references). Account objects provide information about and allow the management of different aspects of the account, such as [account storage](./storage.mdx), diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/keys.mdx b/versioned_docs/version-1.0/language/accounts/keys.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/keys.mdx rename to versioned_docs/version-1.0/language/accounts/keys.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/paths.mdx b/versioned_docs/version-1.0/language/accounts/paths.mdx similarity index 90% rename from versioned_docs/version-current_0.42/0.42/language/accounts/paths.mdx rename to versioned_docs/version-1.0/language/accounts/paths.mdx index c81e87e..2a3bae5 100644 --- a/versioned_docs/version-current_0.42/0.42/language/accounts/paths.mdx +++ b/versioned_docs/version-1.0/language/accounts/paths.mdx @@ -7,7 +7,7 @@ Account storage stores objects under paths. Paths consist of a domain and an identifier. Paths start with the character `/`, followed by the domain, the path separator `/`, -and finally the identifier. +and finally the identifier. The identifier must start with a letter and can only be followed by letters, numbers, or the underscore `_`. For example, the path `/storage/test` has the domain `storage` and the identifier `test`. There are two valid domains: `storage` and `public`. diff --git a/versioned_docs/version-current_0.42/0.42/language/accounts/storage.mdx b/versioned_docs/version-1.0/language/accounts/storage.mdx similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/accounts/storage.mdx rename to versioned_docs/version-1.0/language/accounts/storage.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/attachments.mdx b/versioned_docs/version-1.0/language/attachments.mdx similarity index 61% rename from versioned_docs/version-current_0.42/0.42/language/attachments.mdx rename to versioned_docs/version-1.0/language/attachments.mdx index e4fb948..86f4823 100644 --- a/versioned_docs/version-current_0.42/0.42/language/attachments.mdx +++ b/versioned_docs/version-1.0/language/attachments.mdx @@ -14,11 +14,11 @@ without requiring the original author of the type to plan or account for the int ## Declaring Attachments Attachments are declared with the `attachment` keyword, which would be declared using a new form of composite declaration: -`pub attachment for : { ... }`, where the attachment functions and fields are declared in the body. +`attachment for : { ... }`, where the attachment functions and fields are declared in the body. As such, the following would be examples of legal declarations of attachments: ```cadence -pub attachment Foo for MyStruct { +access(all) attachment Foo for MyStruct { // ... } @@ -36,16 +36,25 @@ Note that the base type may be either a concrete composite type or an interface. In the former case, the attachment is only usable on values specifically of that base type, while in the case of an interface the attachment is usable on any type that conforms to that interface. -As with other type declarations, attachments may only have a `pub` access modifier (if one is present). +Unlike other type declarations, attachments may use either an `access(all)` access modifier, or an `access(M)` modifier, +where `M` is the name of an entitlement mapping. +When attachments are defined with an `access(all)` modifier, +members on the attachment may not use any entitlements in their access modifiers, +and any references to that attachment are always unauthorized. +When attachments are defined with an an [entitlement mapping](./access-control.md), +members on the attachments may use any entitlements in the range of that mapping, +and any references to that attachments will have their authorization depend on the entitlements present on the base type on which they are accessed. The body of the attachment follows the same declaration rules as composites. In particular, they may have both field and function members, -and any field members must be initialized in an `init` function. +and any field members must be initialized in an initializer. Only resource-kinded attachments may have resource members, and such members must be explicitly handled in the `destroy` function. The `self` keyword is available in attachment bodies, but unlike in a composite, `self` is a **reference** type, rather than a composite type: In an attachment declaration for `A`, the type of `self` would be `&A`, rather than `A` like in other composite declarations. +If the attachment declaration uses an `access(all)` access modifier, the `self` reference is always unauthorized, +whereas if it uses an `access(M)` access modifier, the `self` reference is fully-entitled to the range of `M`. If a resource with attachments on it is `destroy`ed, the `destroy` functions of all its attachments are all run in an unspecified order; `destroy` should not rely on the presence of other attachments on the base type in its implementation. @@ -55,29 +64,29 @@ Within the body of an attachment, there is also a `base` keyword available, which contains a reference to the attachment's base value; that is, the composite to which the attachment is attached. Its type, therefore, is a reference to the attachment's declared base type. -So, for an attachment declared `pub attachment Foo for Bar`, the `base` field of `Foo` would have type `&Bar`. +So, for an attachment declared `access(all) attachment Foo for Bar`, the `base` field of `Foo` would have type `&Bar`. So, for example, this would be a valid declaration of an attachment: ``` -pub resource R { - pub let x: Int +access(all) resource R { + access(all) let x: Int init (_ x: Int) { self.x = x } - pub fun foo() { ... } + access(all) fun foo() { ... } } -pub attachment A for R { - pub let derivedX: Int +access(all) attachment A for R { + access(all) let derivedX: Int init (_ scalar: Int) { self.derivedX = base.x * scalar } - pub fun foo() { + access(all) fun foo() { base.foo() } } @@ -86,18 +95,18 @@ pub attachment A for R { For the purposes of external mutation checks or [access control](./access-control.md), the attachment is considered a separate declaration from its base type. -A developer cannot, therefore, access any `priv` fields +A developer cannot, therefore, access any `access(self)` fields (or `access(contract)` fields if the base was defined in a different contract to the attachment) on the `base` value, nor can they mutate any array or dictionary typed fields. ```cadence -pub resource interface SomeInterface { - pub let b: Bool - priv let i: Int - pub let a: [String] +access(all) resource interface SomeInterface { + access(all) let b: Bool + access(self) let i: Int + access(all) let a: [String] } -pub attachment SomeAttachment for SomeContract.SomeStruct { - pub let i: Int +access(all) attachment SomeAttachment for SomeContract.SomeStruct { + access(all) let i: Int init(i: Int) { if base.b { self.i = base.i // cannot access `i` on the `base` value @@ -105,38 +114,65 @@ pub attachment SomeAttachment for SomeContract.SomeStruct { self.i = i } } - pub fun foo() { + access(all) fun foo() { base.a.append("hello") // cannot mutate `a` outside of the composite where it was defined } } ``` +By default, the `base` reference is unauthorized, and thus entitled-access members on the base type are inaccessible in the attachment. +If the author of the attachment wishes to have access to entitled-access members on the base type, +they must declare that their attachment requires certain entitlements to the base, using `require entitlement E` syntax. +Required entitlements must be valid entitlements for the base type, +and requiring entitlements in the attachment declaration will impose additional requirements when the attachment is attached, +as described below. So, for example: + +```cadence +entitlement mapping M { + E -> F +} + +resource R { + access(E) fun foo() { + //... + } +} + +access(M) attachment A for R { + require entitlement E + + access(all) fun bar() { + base.foo() // available because `E` is required above, and thus `base` is type `auth(E) &R`. + } +} +``` + ### Attachment Types -An attachment declared with `pub attachment A for C { ... }` will have a nominal type `A`. +An attachment declared with `access(all) attachment A for C { ... }` will have a nominal type `A`. -It is important to note that attachments are not first class values in Cadence, and as such their usage is limited in certain ways. +It is important to note that attachments are not first class values, and as such their usage is limited in certain ways. In particular, their types cannot appear outside of a reference type. -So, for example, given an attachment declaration `attachment A for X {}`, the types `A`, `A?`, `[A]` and `((): A)` are not valid type annotations, -while `&A`, `&A?`, `[&A]` and `((): &A)` are valid. +So, for example, given an attachment declaration `attachment A for X {}`, the types `A`, `A?`, `[A]` and `fun(): A` are not valid type annotations, +while `&A`, `&A?`, `[&A]` and `fun(): &A` are valid. ## Creating Attachments An attachment is created using an `attach` expression, where the attachment is both initialized and attached to the base value in a single operation. -Attachments are not first-class values in Cadence; they cannot exist independently of a base value, +Attachments are not first-class values; they cannot exist independently of a base value, nor can they be moved around on their own. This means that an `attach` expression is the only place in which an attachment constructor can be called. Tightly coupling the creation and attaching of attachment values helps to make reasoning about attachments simpler for the user. -Also for this reason, resource attachments do not need an expliict `<-` move operator when they appear in an `attach` expression. +Also for this reason, resource attachments do not need an explicit `<-` move operator when they appear in an `attach` expression. An attach expression consists of the `attach` keyword, a constructor call for the attachment value, the `to` keyword, and an expression that evaluates to the base value for that attachment. -Any arguments required by the attachment's `init` function are provided in its constructor call. +Any arguments required by the attachment's initializer are provided in its constructor call. ```cadence -pub resource R {} -pub attachment A for R { +access(all) resource R {} +access(all) attachment A for R { init(x: Int) { //... } @@ -149,15 +185,15 @@ let r2 <- attach A(x: 3) to <-r The expression on the right-hand side of the `to` keyword must evaluate to a composite value whose type is a subtype of the attachment's base, and is evaluated before the call to the constructor on the left side of `to`. -This means that the `base` value is available inside of the attachment's `init` function, +This means that the `base` value is available inside of the attachment's initializer, but it is important to note that the attachment being created will not be accessible on the `base` (see the accessing attachments section below) until after the constructor finishes executing. ```cadence -pub resource interface I {} -pub resource R: I {} -pub attachment A for I {} +access(all) resource interface I {} +access(all) resource R: I {} +access(all) attachment A for I {} // ... let r <- create R() // has type @R @@ -169,6 +205,14 @@ Cadence will raise a runtime error if a user attempts to add an attachment to a The type returned by the `attach` expression is the same type as the expression on the right-hand side of the `to`; attaching an attachment to a value does not change its type. +If an attachment has required entitlements to its base, those entitlements must be explicitly provided in the `attach` expression +using an additional `with` syntax. So, for example, if an attachment `A` declared for `R` requires entitlements `E` and `F`, it can +be attached to an `r` of type `@R` like so: + +```cadence +let rWithA <- attach A() to <-r with (E, F) +``` + ## Accessing Attachments Attachments are accessed on composites via type-indexing: @@ -181,12 +225,12 @@ let a = v[A] // has type `&A?` This syntax requires that `A` is a nominal attachment type, and that `v` has a composite type that is a subtype of `A`'s declared base type. -As mentioned above, attachments are not first-class values in Cadence, +As mentioned above, attachments are not first-class values, so this indexing returns a reference to the attachment on `v`, rather than the attachment itself. If the attachment with the given type does not exist on `v`, this expression returns `nil`. Because the indexed value must be a subtype of the indexing attachment's base type, -the owner of a resource can restrict which attachments can be accessed on references to their resource using restricted types, +the owner of a resource can restrict which attachments can be accessed on references to their resource using interface types, much like they would do with any other field or function. E.g. ```cadence @@ -201,6 +245,31 @@ fun foo(r: &{I}) { Hence, if the owner of a resource wishes to allow others to use a subset of its attachments, they can create a capability to that resource with a borrow type that only allows certain attachments to be accessed. +If an attachment is declared with an `access(all)` modifier, +accessing one this way will always produce an unauthorized reference to the attachment. +However, if the attachment is declared with an `access(M)` modifier, where `M` is some entitlement mapping, +then the authorization of the resulting reference will depend on the authorization of the accessed value. + +So, for example, given a declaration + +```cadence +entitlement E +entitlement F +entitlement G +entitlement H +entitlement mapping M { + E -> F + G -> H +} +resource R {} +access(M) attachment A for R {} +``` + +when `A` is accessed on an owned value of type `@R`, it will be fully-authorized to the domain of `M`, +having a type of `auth(F, H) &A`. +However, if `A` is accessed on an `auth(E) &R` reference, then it will only have a `auth(F) &A` type. +If `A` is accessed on an unauthorized `&R` reference, then it will yield an unauthorized `&A` type. + ## Removing Attachments Attachments can be removed from a value with a `remove` statement. diff --git a/versioned_docs/version-1.0/language/built-in-functions.mdx b/versioned_docs/version-1.0/language/built-in-functions.mdx new file mode 100644 index 0000000..375ebc7 --- /dev/null +++ b/versioned_docs/version-1.0/language/built-in-functions.mdx @@ -0,0 +1,95 @@ +--- +title: Built-in Functions +sidebar_position: 28 +--- + +## `panic` +# + +```cadence +fun panic(_ message: String): Never +``` + +Terminates the program unconditionally +and reports a message which explains why the unrecoverable error occurred. + +```cadence +panic("something went wrong: ...") +``` + +## `assert` + +```cadence +fun assert(_ condition: Bool, message: String) +``` + +Terminates the program if the given condition is false, +and reports a message which explains how the condition is false. +Use this function for internal sanity checks. + +The message argument is optional. + +## `unsafeRandom` + +```cadence +fun unsafeRandom(): UInt64 +``` + +Returns a pseudo-random number. + + +Smart contract developers should be mindful about the limitations of unsafeRandom. +The stream of random numbers produced is potentially unsafe in the following two regards: + +1. The sequence of random numbers is potentially predictable by transactions within the same block +and by other smart contracts calling into your smart contract. +2. A transaction calling into your smart contract can potentially bias the sequence of random numbers which +your smart contract internally generates. + +The Flow project is working towards removing these limitations incrementally. +Once Flow addressed these points and randomness is safe, +the "unsafe" qualifier is going to get removed. + +Nevertheless, there is an additional safety-relevant aspect that developers need to be mindful about: + +A transaction can atomically revert all its actions at any time. +Therefore, it is possible for a transaction calling into a smart contract +to post-select favorable results and revert the transaction for unfavorable results. + +This limitation is inherent to any smart contract platform that allows transactions to roll back atomically +and cannot be solved through safe randomness alone. +Providing additional Cadence language primitives to simplify this challenge for developers is on the roadmap. +Nevertheless, with safe randomness, points 1 and 2 above resolved, +developers can prevent clients from post-select favorable outcomes using approaches such as described in the +[Smart Contract Security Best Practices](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/). + + +## `RLP` + +Recursive Length Prefix (RLP) serialization allows the encoding of arbitrarily nested arrays of binary data. + +Cadence provides RLP decoding functions in the built-in `RLP` contract, which does not need to be imported. + +### `decodeString` + +```cadence +fun decodeString(_ input: [UInt8]): [UInt8] +``` + +Decodes an RLP-encoded byte array. RLP calls this a "string." +The byte array should only contain of a single encoded value for a string. +If the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. +If the function encounters any error while decoding, the program aborts. + +### `decodeList` + +```cadence +fun decodeList(_ input: [UInt8]): [[UInt8]]` +``` + +Decodes an RLP-encoded list into an array of RLP-encoded items. + +Note that this function does not recursively decode, so each element of the resulting array is RLP-encoded data. +The byte array should only contain of a single encoded value for a list. +If the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. +If the function encounters any error while decoding, the program aborts. diff --git a/versioned_docs/version-1.0/language/capabilities.md b/versioned_docs/version-1.0/language/capabilities.md new file mode 100644 index 0000000..f001a3d --- /dev/null +++ b/versioned_docs/version-1.0/language/capabilities.md @@ -0,0 +1,70 @@ +--- +title: Capabilities +sidebar_position: 14 +--- + +Cadence supports [capability-based security](https://en.wikipedia.org/wiki/Capability-based_security) +through the [object-capability model](https://en.wikipedia.org/wiki/Object-capability_model). + +A capability in Cadence is a value that represents the right +to access an object and perform certain operations on it. +A capability specifies _what_ can be accessed, and _how_ it can be accessed. + +Capabilities are unforgeable, transferable, and revocable. + +Capabilities can be storage capabilities or account capabilities: +- **Storage capabilities** grant access to [objects in account storage](./accounts/storage.mdx), +via [paths](./accounts/paths.mdx) +- **Account capabilities** grant access to [accounts](./accounts/index.mdx) + +Capabilities can be borrowed to get a [reference](./references.mdx) to the stored object or the account it refers to. + +Capabilities have the type `Capability`. +The type parameter specifies the kind of reference that can be obtained when borrowing the capability. +The type specifies the associated set of access rights through [entitlements](./access-control.md): +the reference type of the capability can be authorized, +which grants the owner of the capability the ability to access the fields and functions of the target +which require the given entitlements. + +For example, a capability which has type `Capability` +grants access to an account, and allows saving a value into the account. + +Each capability has an ID. +The ID is unique **per account/address**. + +Capabilities are created and managed through [capability controllers](./accounts/capabilities.mdx). + +## `Capability` + +```cadence +access(all) +struct Capability { + /// The address of the account which the capability targets. + access(all) + let address: Address + + /// The ID of the capability. + access(all) + let id: UInt64 + + /// Returns a reference to the targeted object. + /// + /// If the capability is revoked, the function returns nil. + /// + /// If the capability targets an object in account storage, + /// and and no object is stored at the target storage path, + /// the function returns nil. + /// + /// If the targeted object cannot be borrowed using the given type, + /// the function panics. + /// + access(all) + view fun borrow(): T? + + /// Returns true if the capability currently targets an object + /// that satisfies the given type, i.e. could be borrowed using the given type. + /// + access(all) + view fun check(): Bool +} +``` diff --git a/versioned_docs/version-current_0.42/0.42/language/composite-types.mdx b/versioned_docs/version-1.0/language/composite-types.mdx similarity index 89% rename from versioned_docs/version-current_0.42/0.42/language/composite-types.mdx rename to versioned_docs/version-1.0/language/composite-types.mdx index f3dbe67..94533ad 100644 --- a/versioned_docs/version-current_0.42/0.42/language/composite-types.mdx +++ b/versioned_docs/version-1.0/language/composite-types.mdx @@ -57,11 +57,11 @@ and resources are declared using the `resource` keyword. The keyword is followed by the name. ```cadence -pub struct SomeStruct { +access(all) struct SomeStruct { // ... } -pub resource SomeResource { +access(all) resource SomeResource { // ... } ``` @@ -141,9 +141,9 @@ that is to be initialized. If a composite type is to be stored, all its field types must be storable. Non-storable types are: - Functions -- [Accounts (`AuthAccount` / `PublicAccount`)](./accounts.mdx) +- [Accounts (`Account`)](./accounts/index.mdx) - [Transactions](./transactions.md) -- [References](./references.md): References are ephemeral. +- [References](./references.mdx): References are ephemeral. Consider [storing a capability and borrowing it](./capabilities.md) when needed instead. Fields can be read (if they are constant or variable) and set (if they are variable), @@ -156,14 +156,14 @@ and the name of the field. // // Both fields are initialized through the initializer. // -// The public access modifier `pub` is used in this example to allow +// The public access modifier `access(all)` is used in this example to allow // the fields to be read in outer scopes. Fields can also be declared // private so they cannot be accessed in outer scopes. // Access control will be explained in a later section. // -pub struct Token { - pub let id: Int - pub var balance: Int +access(all) struct Token { + access(all) let id: Int + access(all) var balance: Int init(id: Int, balance: Int) { self.id = id @@ -175,19 +175,19 @@ pub struct Token { Note that it is invalid to provide the initial value for a field in the field declaration. ```cadence -pub struct StructureWithConstantField { +access(all) struct StructureWithConstantField { // Invalid: It is invalid to provide an initial value in the field declaration. // The field must be initialized by setting the initial value in the initializer. // - pub let id: Int = 1 + access(all) let id: Int = 1 } ``` The field access syntax must be used to access fields – fields are not available as variables. ```cadence -pub struct Token { - pub let id: Int +access(all) struct Token { + access(all) let id: Int init(initialID: Int) { // Invalid: There is no variable with the name `id` available. @@ -201,8 +201,8 @@ pub struct Token { The initializer is **not** automatically derived from the fields, it must be explicitly declared. ```cadence -pub struct Token { - pub let id: Int +access(all) struct Token { + access(all) let id: Int // Invalid: Missing initializer initializing field `id`. } @@ -229,6 +229,23 @@ token.id = 23 Initializers do not support overloading. +Initializers can also be declared with the `view` keyword, to indicate that they do not perform any mutating operations, +and to allow them to be called from within other `view` functions. +In an initializer, writes to `self` are considered `view` (unlike within other composite functions), +as the value being constructed here is by definition local to the context calling the initializer. + +```cadence +access(all) struct Token { + access(all) let id: Int + access(all) var balance: Int + + view init(id: Int, balance: Int) { + self.id = id + self.balance = balance + } +} +``` + ## Composite Type Functions Composite types may contain functions. @@ -239,9 +256,9 @@ that the function is called on. // Declare a structure named "Rectangle", which represents a rectangle // and has variable fields for the width and height. // -pub struct Rectangle { - pub var width: Int - pub var height: Int +access(all) struct Rectangle { + access(all) var width: Int + access(all) var height: Int init(width: Int, height: Int) { self.width = width @@ -251,7 +268,7 @@ pub struct Rectangle { // Declare a function named "scale", which scales // the rectangle by the given factor. // - pub fun scale(factor: Int) { + access(all) fun scale(factor: Int) { self.width = self.width * factor self.height = self.height * factor } @@ -275,14 +292,14 @@ the types are only compatible if their names match. ```cadence // Declare a structure named `A` which has a function `test` -// which has type `((): Void)`. +// which has type `fun(): Void`. // struct A { fun test() {} } // Declare a structure named `B` which has a function `test` -// which has type `((): Void)`. +// which has type `fun(): Void`. // struct B { fun test() {} @@ -319,8 +336,8 @@ Accessing a field or calling a function of a structure does not copy it. ```cadence // Declare a structure named `SomeStruct`, with a variable integer field. // -pub struct SomeStruct { - pub var value: Int +access(all) struct SomeStruct { + access(all) var value: Int init(value: Int) { self.value = value @@ -369,18 +386,18 @@ of an optional composite type. ```cadence // Declare a struct with a field and method. -pub struct Value { - pub var number: Int +access(all) struct Value { + access(all) var number: Int init() { self.number = 2 } - pub fun set(new: Int) { + access(all) fun set(new: Int) { self.number = new } - pub fun setAndReturn(new: Int): Int { + access(all) fun setAndReturn(new: Int): Int { self.number = new return new } @@ -432,18 +449,18 @@ of an optional composite type. ```cadence // Declare a struct with a field and method. -pub struct Value { - pub var number: Int +access(all) struct Value { + access(all) var number: Int init() { self.number = 2 } - pub fun set(new: Int) { + access(all) fun set(new: Int) { self.number = new } - pub fun setAndReturn(new: Int): Int { + access(all) fun setAndReturn(new: Int): Int { self.number = new return new } diff --git a/versioned_docs/version-current_0.42/0.42/language/constants-and-variables.md b/versioned_docs/version-1.0/language/constants-and-variables.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/constants-and-variables.md rename to versioned_docs/version-1.0/language/constants-and-variables.md diff --git a/versioned_docs/version-current_0.42/0.42/language/contract-updatability.md b/versioned_docs/version-1.0/language/contract-updatability.md similarity index 83% rename from versioned_docs/version-current_0.42/0.42/language/contract-updatability.md rename to versioned_docs/version-1.0/language/contract-updatability.md index bb5addf..21b293d 100644 --- a/versioned_docs/version-current_0.42/0.42/language/contract-updatability.md +++ b/versioned_docs/version-1.0/language/contract-updatability.md @@ -4,7 +4,7 @@ sidebar_position: 23 --- ## Introduction -A [contract](./contracts.mdx) in Cadence is a collection of data (its state) and +A [contract](./contracts.mdx) is a collection of data (its state) and code (its functions) that lives in the contract storage area of an account. When a contract is updated, it is important to make sure that the changes introduced do not lead to runtime inconsistencies for already stored data. @@ -74,16 +74,16 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - pub contract Foo { - pub var a: String - pub var b: Int + access(all) contract Foo { + access(all) var a: String + access(all) var b: Int } // Updated contract - pub contract Foo { - pub var a: String + access(all) contract Foo { + access(all) var a: String } ``` - It leaves data for the removed field unused at the storage, as it is no longer accessible. @@ -93,17 +93,17 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - pub contract Foo { - pub var a: String - pub var b: Int + access(all) contract Foo { + access(all) var a: String + access(all) var b: Int } // Updated contract - pub contract Foo { - pub var b: Int - pub var a: String + access(all) contract Foo { + access(all) var b: Int + access(all) var a: String } ``` @@ -111,15 +111,15 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - pub contract Foo { - pub var a: String + access(all) contract Foo { + access(all) var a: String } // Updated contract - pub contract Foo { - priv var a: String // access modifier changed to 'priv' + access(all) contract Foo { + access(self) var a: String // access modifier changed to 'access(self)' } ``` @@ -128,16 +128,16 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - pub contract Foo { - pub var a: String + access(all) contract Foo { + access(all) var a: String } // Updated contract - pub contract Foo { - pub var a: String - pub var b: Int // Invalid new field + access(all) contract Foo { + access(all) var a: String + access(all) var b: Int // Invalid new field } ``` - Initializer of a contract only run once, when the contract is deployed for the first time. It does not rerun @@ -150,15 +150,15 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing contract - pub contract Foo { - pub var a: String + access(all) contract Foo { + access(all) var a: String } // Updated contract - pub contract Foo { - pub var a: Int // Invalid type change + access(all) contract Foo { + access(all) var a: Int // Invalid type change } ``` - In an already stored contract, the field `a` would have a value of type `String`. @@ -180,13 +180,13 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing struct - pub struct Foo { + access(all) struct Foo { } // Upated struct - pub struct Foo: T { + access(all) struct Foo: T { } ``` - However, if adding a conformance also requires changing the existing structure (e.g: adding a new field that is @@ -203,26 +203,26 @@ A field may belong to a contract, struct, resource, or interface. ```cadence // Existing struct - pub struct Foo { + access(all) struct Foo { } // Changed to a struct interface - pub struct interface Foo { // Invalid type declaration change + access(all) struct interface Foo { // Invalid type declaration change } ``` - Removing an interface conformance of a struct/resource is not valid. ```cadence // Existing struct - pub struct Foo: T { + access(all) struct Foo: T { } // Upated struct - pub struct Foo { + access(all) struct Foo { } ``` @@ -253,17 +253,17 @@ Below sections describes the restrictions imposed on updating the members of a s ```cadence // Existing enum with `Int` raw type - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum with `UInt8` raw type - pub enum Color: UInt8 { // Invalid change of raw type - pub case RED - pub case BLUE + access(all) enum Color: UInt8 { // Invalid change of raw type + access(all) case RED + access(all) case BLUE } ``` - When the enum value is stored, the raw value associated with the enum-case gets stored. @@ -282,18 +282,18 @@ it originally was (type confusion). ```cadence // Existing enum - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum - pub enum Color: Int { - pub case RED - pub case BLUE - pub case GREEN // valid new enum-case at the bottom + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE + access(all) case GREEN // valid new enum-case at the bottom } ``` #### Invalid Changes @@ -301,35 +301,35 @@ it originally was (type confusion). ```cadence // Existing enum - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum - pub enum Color: Int { - pub case RED - pub case GREEN // invalid new enum-case in the middle - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case GREEN // invalid new enum-case in the middle + access(all) case BLUE } ``` - Changing the name of an enum-case is invalid. ```cadence // Existing enum - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum - pub enum Color: Int { - pub case RED - pub case GREEN // invalid change of names + access(all) enum Color: Int { + access(all) case RED + access(all) case GREEN // invalid change of names } ``` - Previously stored raw values for `Color.BLUE` now represents `Color.GREEN`. i.e: The stored values have changed @@ -342,16 +342,16 @@ it originally was (type confusion). ```cadence // Existing enum - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum - pub enum Color: Int { - pub case RED + access(all) enum Color: Int { + access(all) case RED // invalid removal of `case BLUE` } @@ -360,17 +360,17 @@ it originally was (type confusion). ```cadence // Existing enum - pub enum Color: Int { - pub case RED - pub case BLUE + access(all) enum Color: Int { + access(all) case RED + access(all) case BLUE } // Updated enum - pub enum Color: UInt8 { - pub case BLUE // invalid change of order - pub case RED + access(all) enum Color: UInt8 { + access(all) case BLUE // invalid change of order + access(all) case RED } ``` - Raw value of an enum is implicit, and corresponds to the defined order. diff --git a/versioned_docs/version-1.0/language/contracts.mdx b/versioned_docs/version-1.0/language/contracts.mdx new file mode 100644 index 0000000..e747347 --- /dev/null +++ b/versioned_docs/version-1.0/language/contracts.mdx @@ -0,0 +1,258 @@ +--- +title: Contracts +sidebar_position: 22 +--- + +A contract is a collection of type definitions, +data (its state), and code (its functions), +that is stored in the contract storage area of an account. + +Contracts are where all composite types interfaces for these types have to be defined. +Therefore, an object of one of these types cannot exist +without having been defined in a deployed Cadence contract. + +Contracts can be deployed to accounts, updated, and removed from accounts +using the `contracts` object of [authorized accounts](./accounts/index.mdx). +See the [account contracts page](./accounts/contracts.mdx) +for more information about these operations. + +Contracts are types. +They are similar to composite types, but are stored differently than +structs or resources and cannot be used as values, copied, or moved +like resources or structs. + +Contracts stay in an account's contract storage area +and can only be added, updated, or removed by the account owner with special commands. + +Contracts are declared using the `contract` keyword. +The keyword is followed by the name of the contract. + +```cadence +access(all) +contract SomeContract { + // ... +} +``` + +Contracts cannot be nested in each other. + +```cadence +access(all) +contract Invalid { + + // Invalid: Contracts cannot be nested in any other type. + // + access(all) + contract Nested { + // ... + } +} +``` + +One of the simplest forms of a contract would just be one with a state field, +a function, and an initializer that initializes the field: + +```cadence +access(all) +contract HelloWorld { + + // Declare a stored state field in HelloWorld + // + access(all) + let greeting: String + + // Declare a function that can be called by anyone + // who imports the contract + // + access(all) + fun hello(): String { + return self.greeting + } + + init() { + self.greeting = "Hello World!" + } +} +``` + +Transactions and other contracts can interact with contracts +by importing them at the beginning of a transaction or contract definition. + +Anyone could call the above contract's `hello` function by importing +the contract from the account it was deployed to and using the imported +object to call the hello function. + +```cadence +import HelloWorld from 0x42 + +// Invalid: The contract does not know where hello comes from +// +log(hello()) // Error + +// Valid: Using the imported contract object to call the hello +// function +// +log(HelloWorld.hello()) // prints "Hello World!" + +// Valid: Using the imported contract object to read the greeting +// field. +log(HelloWorld.greeting) // prints "Hello World!" + +// Invalid: Cannot call the init function after the contract has been created. +// +HelloWorld.init() // Error +``` + +There can be any number of contracts per account +and they can include an arbitrary amount of data. +This means that a contract can have any number of fields, functions, and type definitions, +but they have to be in the contract and not another top-level definition. + +```cadence +// Invalid: Top-level declarations are restricted to only be contracts +// or contract interfaces. Therefore, all of these would be invalid +// if they were deployed to the account contract storage and +// the deployment would be rejected. +// +access(all) resource Vault {} +access(all) struct Hat {} +access(all) fun helloWorld(): String {} +let num: Int +``` + +Another important feature of contracts is that instances of resources and events +that are declared in contracts can only be created/emitted within functions or types +that are declared in the same contract. + +It is not possible create instances of resources and events outside the contract. + +The contract below defines a resource interface `Receiver` and a resource `Vault` +that implements that interface. The way this example is written, +there is no way to create this resource, so it would not be usable. + +```cadence +// Valid +access(all) contract FungibleToken { + + access(all) resource interface Receiver { + + access(all) balance: Int + + access(all) fun deposit(from: @{Receiver}) { + pre { + from.balance > 0: + "Deposit balance needs to be positive!" + } + post { + self.balance == before(self.balance) + before(from.balance): + "Incorrect amount removed" + } + } + } + + access(all) resource Vault: Receiver { + + // keeps track of the total balance of the accounts tokens + access(all) var balance: Int + + init(balance: Int) { + self.balance = balance + } + + // withdraw subtracts amount from the vaults balance and + // returns a vault object with the subtracted balance + access(all) fun withdraw(amount: Int): @Vault { + self.balance = self.balance - amount + return <-create Vault(balance: amount) + } + + // deposit takes a vault object as a parameter and adds + // its balance to the balance of the Account's vault, then + // destroys the sent vault because its balance has been consumed + access(all) fun deposit(from: @{Receiver}) { + self.balance = self.balance + from.balance + destroy from + } + } +} +``` + +If a user tried to run a transaction that created an instance of the `Vault` type, +the type checker would not allow it because only code in the `FungibleToken` +contract can create new `Vault`s. + +```cadence +import FungibleToken from 0x42 + +// Invalid: Cannot create an instance of the `Vault` type outside +// of the contract that defines `Vault` +// +let newVault <- create FungibleToken.Vault(balance: 10) +``` + +## Account access + +Contracts can access the account they are deployed to: +contracts have the implicit field named `account` +which is only accessible within the contract. + +```cadence +let account: auth(Storage, Keys, Contracts, Inbox, Capabilities) &Account`, +``` + +The account reference is fully entitled, +so grants access to the account's storage, keys, contracts, etc. + +For example, this gives the contract the ability to write to the account's storage +when the contract is initialized. + +```cadence +init(balance: Int) { + self.account.storage.save( + <-create Vault(balance: 1000), + to: /storage/initialVault + ) +} +``` + +## Contract interfaces + +Like composite types, contracts can have interfaces that specify rules +about their behavior, their types, and the behavior of their types. + +Contract interfaces have to be declared globally. +Declarations cannot be nested in other types. + +Contract interfaces may not declare concrete types (other than events), but they can declare interfaces. +If a contract interface declares an interface type, the implementing contract +does not have to also define that interface. +They can refer to that nested interface by saying `{ContractInterfaceName}.{NestedInterfaceName}` + +```cadence +// Declare a contract interface that declares an interface and a resource +// that needs to implement that interface in the contract implementation. +// +access(all) contract interface InterfaceExample { + + // Implementations do not need to declare this + // They refer to it as InterfaceExample.NestedInterface + // + access(all) resource interface NestedInterface {} + + // Implementations must declare this type + // + access(all) resource Composite: NestedInterface {} +} + +access(all) contract ExampleContract: InterfaceExample { + + // The contract doesn't need to redeclare the `NestedInterface` interface + // because it is already declared in the contract interface + + // The resource has to refer to the resource interface using the name + // of the contract interface to access it + // + access(all) resource Composite: InterfaceExample.NestedInterface { + } +} +``` diff --git a/versioned_docs/version-current_0.42/0.42/language/control-flow.md b/versioned_docs/version-1.0/language/control-flow.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/control-flow.md rename to versioned_docs/version-1.0/language/control-flow.md diff --git a/versioned_docs/version-current_0.42/0.42/language/core-events.md b/versioned_docs/version-1.0/language/core-events.md similarity index 89% rename from versioned_docs/version-current_0.42/0.42/language/core-events.md rename to versioned_docs/version-1.0/language/core-events.md index 1115b83..2caff35 100644 --- a/versioned_docs/version-current_0.42/0.42/language/core-events.md +++ b/versioned_docs/version-1.0/language/core-events.md @@ -1,12 +1,12 @@ --- title: Core Events -sidebar_position: 25 +sidebar_position: 26 --- Core events are events emitted directly from the FVM (Flow Virtual Machine). The events have the same name on all networks and do not follow the standard naming (they have no address). -Refer to the [`PublicKey` section](./crypto.mdx#publickey) for more details on the information provided for account key events. +Refer to the [public key section](./crypto.mdx#public-keys) for more details on the information provided for account key events. ### Account Created @@ -14,9 +14,8 @@ Event that is emitted when a new account gets created. Event name: `flow.AccountCreated` - ```cadence -pub event AccountCreated(address: Address) +access(all) event AccountCreated(address: Address) ``` | Field | Type | Description | @@ -31,7 +30,7 @@ Event that is emitted when a key gets added to an account. Event name: `flow.AccountKeyAdded` ```cadence -pub event AccountKeyAdded( +access(all) event AccountKeyAdded( address: Address, publicKey: PublicKey ) @@ -50,7 +49,7 @@ Event that is emitted when a key gets removed from an account. Event name: `flow.AccountKeyRemoved` ```cadence -pub event AccountKeyRemoved( +access(all) event AccountKeyRemoved( address: Address, publicKey: PublicKey ) @@ -69,7 +68,7 @@ Event that is emitted when a contract gets deployed to an account. Event name: `flow.AccountContractAdded` ```cadence -pub event AccountContractAdded( +access(all) event AccountContractAdded( address: Address, codeHash: [UInt8], contract: String @@ -89,7 +88,7 @@ Event that is emitted when a contract gets updated on an account. Event name: `flow.AccountContractUpdated` ```cadence -pub event AccountContractUpdated( +access(all) event AccountContractUpdated( address: Address, codeHash: [UInt8], contract: String @@ -110,7 +109,7 @@ Event that is emitted when a contract gets removed from an account. Event name: `flow.AccountContractRemoved` ```cadence -pub event AccountContractRemoved( +access(all) event AccountContractRemoved( address: Address, codeHash: [UInt8], contract: String @@ -130,7 +129,7 @@ Event that is emitted when a Capability is published from an account. Event name: `flow.InboxValuePublished` ```cadence -pub event InboxValuePublished(provider: Address, recipient: Address, name: String, type: Type) +access(all) event InboxValuePublished(provider: Address, recipient: Address, name: String, type: Type) ``` | Field | Type | Description | @@ -151,7 +150,7 @@ Event that is emitted when a Capability is unpublished from an account. Event name: `flow.InboxValueUnpublished` ```cadence -pub event InboxValueUnpublished(provider: Address, name: String) +access(all) event InboxValueUnpublished(provider: Address, name: String) ``` | Field | Type | Description | @@ -170,7 +169,7 @@ Event that is emitted when a Capability is claimed by an account. Event name: `flow.InboxValueClaimed` ```cadence -pub event InboxValueClaimed(provider: Address, recipient: Address, name: String) +access(all) event InboxValueClaimed(provider: Address, recipient: Address, name: String) ``` | Field | Type | Description | diff --git a/versioned_docs/version-current_0.42/0.42/language/crypto.mdx b/versioned_docs/version-1.0/language/crypto.mdx similarity index 91% rename from versioned_docs/version-current_0.42/0.42/language/crypto.mdx rename to versioned_docs/version-1.0/language/crypto.mdx index 6889747..3fad2ae 100644 --- a/versioned_docs/version-current_0.42/0.42/language/crypto.mdx +++ b/versioned_docs/version-1.0/language/crypto.mdx @@ -3,24 +3,24 @@ title: Crypto sidebar_position: 30 --- -## Hashing +## Hash algorithms -The built-in enum `HashAlgorithm` provides the set of hashing algorithms that -are supported by the language natively. +The built-in [enumeration](./enumerations.md) `HashAlgorithm` +provides the set of supported hashing algorithms. ```cadence -pub enum HashAlgorithm: UInt8 { +access(all) enum HashAlgorithm: UInt8 { /// SHA2_256 is SHA-2 with a 256-bit digest (also referred to as SHA256). - pub case SHA2_256 = 1 + access(all) case SHA2_256 = 1 /// SHA2_384 is SHA-2 with a 384-bit digest (also referred to as SHA384). - pub case SHA2_384 = 2 + access(all) case SHA2_384 = 2 /// SHA3_256 is SHA-3 with a 256-bit digest. - pub case SHA3_256 = 3 + access(all) case SHA3_256 = 3 /// SHA3_384 is SHA-3 with a 384-bit digest. - pub case SHA3_384 = 4 + access(all) case SHA3_384 = 4 /// KMAC128_BLS_BLS12_381 is an instance of KECCAK Message Authentication Code (KMAC128) mac algorithm. /// Although this is a MAC algorithm, KMAC is included in this list as it can be used hash @@ -29,17 +29,17 @@ pub enum HashAlgorithm: UInt8 { /// It is a customized version of KMAC128 that is compatible with the hashing to curve /// used in BLS signatures. /// It is the same hasher used by signatures in the internal Flow protocol. - pub case KMAC128_BLS_BLS12_381 = 5 + access(all) case KMAC128_BLS_BLS12_381 = 5 /// KECCAK_256 is the legacy Keccak algorithm with a 256-bits digest, as per the original submission to the NIST SHA3 competition. /// KECCAK_256 is different than SHA3 and is used by Ethereum. - pub case KECCAK_256 = 6 + access(all) case KECCAK_256 = 6 /// Returns the hash of the given data - pub fun hash(_ data: [UInt8]): [UInt8] + access(all) fun hash(_ data: [UInt8]): [UInt8] /// Returns the hash of the given data and tag - pub fun hashWithTag(_ data: [UInt8], tag: string): [UInt8] + access(all) fun hashWithTag(_ data: [UInt8], tag: string): [UInt8] } ``` @@ -89,38 +89,43 @@ To define the MAC instance, `KMAC128(customizer, key, data, length)` is instanci - `data` is the input data to hash. - `length` is 1024 bytes. -## Signing Algorithms +## Signature algorithms -The built-in enum `SignatureAlgorithm` provides the set of signing algorithms that -are supported by the language natively. +The built-in [enumeration](./enumerations.md) `SignatureAlgorithm` +provides the set of supported signature algorithms. ```cadence -pub enum SignatureAlgorithm: UInt8 { +access(all) enum SignatureAlgorithm: UInt8 { /// ECDSA_P256 is ECDSA on the NIST P-256 curve. - pub case ECDSA_P256 = 1 + access(all) case ECDSA_P256 = 1 /// ECDSA_secp256k1 is ECDSA on the secp256k1 curve. - pub case ECDSA_secp256k1 = 2 + access(all) case ECDSA_secp256k1 = 2 /// BLS_BLS12_381 is BLS signature scheme on the BLS12-381 curve. /// The scheme is set-up so that signatures are in G_1 (subgroup of the curve over the prime field) /// while public keys are in G_2 (subgroup of the curve over the prime field extension). - pub case BLS_BLS12_381 = 3 + access(all) case BLS_BLS12_381 = 3 } ``` -## PublicKey +## Public keys `PublicKey` is a built-in structure that represents a cryptographic public key of a signature scheme. ```cadence +access(all) struct PublicKey { + access(all) let publicKey: [UInt8] + + access(all) let signatureAlgorithm: SignatureAlgorithm /// Verifies a signature under the given tag, data and public key. /// It uses the given hash algorithm to hash the tag and data. - pub fun verify( + access(all) + view fun verify( signature: [UInt8], signedData: [UInt8], domainSeparationTag: String, @@ -131,7 +136,8 @@ struct PublicKey { /// This function is only implemented if the signature algorithm /// of the public key is BLS (BLS_BLS12_381). /// If called with any other signature algorithm, the program aborts - pub fun verifyPoP(_ proof: [UInt8]): Bool + access(all) + view fun verifyPoP(_ proof: [UInt8]): Bool } ``` @@ -271,7 +277,7 @@ available to protect BLS verification. Cadence provides the proof of possession The proof of possession of private key is a BLS signature over the public key itself. The PoP signature follows the same requirements of a BLS signature (detailed in [Signature verification](#Signature-verification)), except it uses a special domain separation tag. The key expected to be used in KMAC128 is the UTF-8 encoding of `"BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"`. -The expected message to be signed by the PoP is the serialization of the BLS public key corresponding to the signing private key ([serialization details](#PublicKey)). +The expected message to be signed by the PoP is the serialization of the BLS public key corresponding to the signing private key ([serialization details](#public-keys)). The PoP can only be verified using the `PublicKey` method `verifyPoP`. ### BLS signature aggregation @@ -328,7 +334,7 @@ For example, to verify two signatures with equal weights for some signed data: ```cadence import Crypto -pub fun test main() { +access(all) fun test main() { let keyList = Crypto.KeyList() let publicKeyA = PublicKey( @@ -380,12 +386,12 @@ pub fun test main() { The API of the Crypto contract related to key lists is: ```cadence -pub struct KeyListEntry { - pub let keyIndex: Int - pub let publicKey: PublicKey - pub let hashAlgorithm: HashAlgorithm - pub let weight: UFix64 - pub let isRevoked: Bool +access(all) struct KeyListEntry { + access(all) let keyIndex: Int + access(all) let publicKey: PublicKey + access(all) let hashAlgorithm: HashAlgorithm + access(all) let weight: UFix64 + access(all) let isRevoked: Bool init( keyIndex: Int, @@ -396,12 +402,12 @@ pub struct KeyListEntry { ) } -pub struct KeyList { +access(all) struct KeyList { init() /// Adds a new key with the given weight - pub fun add( + access(all) fun add( _ publicKey: PublicKey, hashAlgorithm: HashAlgorithm, weight: UFix64 @@ -409,22 +415,22 @@ pub struct KeyList { /// Returns the key at the given index, if it exists. /// Revoked keys are always returned, but they have `isRevoked` field set to true - pub fun get(keyIndex: Int): KeyListEntry? + access(all) fun get(keyIndex: Int): KeyListEntry? /// Marks the key at the given index revoked, but does not delete it - pub fun revoke(keyIndex: Int) + access(all) fun revoke(keyIndex: Int) /// Returns true if the given signatures are valid for the given signed data - pub fun verify( + access(all) fun verify( signatureSet: [KeyListSignature], signedData: [UInt8] ): Bool } -pub struct KeyListSignature { - pub let keyIndex: Int - pub let signature: [UInt8] +access(all) struct KeyListSignature { + access(all) let keyIndex: Int + access(all) let signature: [UInt8] - pub init(keyIndex: Int, signature: [UInt8]) + access(all) init(keyIndex: Int, signature: [UInt8]) } ``` diff --git a/versioned_docs/version-current_0.42/0.42/language/enumerations.md b/versioned_docs/version-1.0/language/enumerations.md similarity index 94% rename from versioned_docs/version-current_0.42/0.42/language/enumerations.md rename to versioned_docs/version-1.0/language/enumerations.md index c877a55..137094c 100644 --- a/versioned_docs/version-current_0.42/0.42/language/enumerations.md +++ b/versioned_docs/version-1.0/language/enumerations.md @@ -1,6 +1,6 @@ --- title: Enumerations -sidebar_position: 15 +sidebar_position: 16 --- Enumerations are sets of symbolic names bound to unique, constant values, @@ -34,10 +34,10 @@ Enum cases can be compared using the equality operators `==` and `!=`. // Declare an enum named `Color` which has the raw value type `UInt8`, // and declare three enum cases: `red`, `green`, and `blue` // -pub enum Color: UInt8 { - pub case red - pub case green - pub case blue +access(all) enum Color: UInt8 { + access(all) case red + access(all) case green + access(all) case blue } // Declare a variable which has the enum type `Color` and initialize // it to the enum case `blue` of the enum diff --git a/versioned_docs/version-current_0.42/0.42/language/environment-information.md b/versioned_docs/version-1.0/language/environment-information.md similarity index 91% rename from versioned_docs/version-current_0.42/0.42/language/environment-information.md rename to versioned_docs/version-1.0/language/environment-information.md index 2f9f4d7..a284754 100644 --- a/versioned_docs/version-current_0.42/0.42/language/environment-information.md +++ b/versioned_docs/version-1.0/language/environment-information.md @@ -34,26 +34,26 @@ To get information about a block, the functions `getCurrentBlock` and `getBlock` The `Block` type contains the identifier, height, and timestamp: ```cadence -pub struct Block { +access(all) struct Block { /// The ID of the block. /// /// It is essentially the hash of the block. /// - pub let id: [UInt8; 32] + access(all) let id: [UInt8; 32] /// The height of the block. /// /// If the blockchain is viewed as a tree with the genesis block at the root, // the height of a node is the number of edges between the node and the genesis block /// - pub let height: UInt64 + access(all) let height: UInt64 /// The view of the block. /// /// It is a detail of the consensus algorithm. It is a monotonically increasing integer /// and counts rounds in the consensus algorithm. It is reset to zero at each spork. /// - pub let view: UInt64 + access(all) let view: UInt64 /// The timestamp of the block. /// @@ -62,7 +62,7 @@ pub struct Block { /// NOTE: It is included by the proposer, there are no guarantees on how much the time stamp can deviate from the true time the block was published. /// Consider observing blocks’ status changes off-chain yourself to get a more reliable value. /// - pub let timestamp: UFix64 + access(all) let timestamp: UFix64 } ``` diff --git a/versioned_docs/version-current_0.42/0.42/language/events.md b/versioned_docs/version-1.0/language/events.md similarity index 96% rename from versioned_docs/version-current_0.42/0.42/language/events.md rename to versioned_docs/version-1.0/language/events.md index 376d5c8..40b50a5 100644 --- a/versioned_docs/version-current_0.42/0.42/language/events.md +++ b/versioned_docs/version-1.0/language/events.md @@ -1,6 +1,6 @@ --- title: Events -sidebar_position: 24 +sidebar_position: 25 --- Events are special values that can be emitted during the execution of a program. @@ -28,7 +28,7 @@ Events cannot be declared globally or within resource or struct types. // event GlobalEvent(field: Int) -pub contract Events { +access(all) contract Events { // Event with explicit argument labels // event BarEvent(labelA fieldA: Int, labelB fieldB: Int) @@ -46,7 +46,7 @@ pub contract Events { To emit an event from a program, use the `emit` statement: ```cadence -pub contract Events { +access(all) contract Events { event FooEvent(x: Int, y: Int) // Event with argument labels diff --git a/versioned_docs/version-current_0.42/0.42/language/functions.mdx b/versioned_docs/version-1.0/language/functions.mdx similarity index 78% rename from versioned_docs/version-current_0.42/0.42/language/functions.mdx rename to versioned_docs/version-1.0/language/functions.mdx index 5dd0b7b..67742fe 100644 --- a/versioned_docs/version-current_0.42/0.42/language/functions.mdx +++ b/versioned_docs/version-1.0/language/functions.mdx @@ -216,7 +216,7 @@ except that function expressions have no name, i.e., they are anonymous. // // The function multiplies a number by two when it is called. // -// This function's type is `((Int): Int)`. +// This function's type is `fun (Int): Int`. // let double = fun (_ x: Int): Int { @@ -224,6 +224,103 @@ let double = } ``` +## View Functions + +Functions can be annotated as `view` to indicate that they do not modify any external state or any account state. +A `view` annotation can be added to the beginning of a function declaration or expression like so: + +```cadence +view access(all) fun foo(): Void {} +let x = view fun(): Void {} +access(all) struct S { + view access(all) fun foo(): Void {} + view init() +} +``` + +All functions that do not have a `view` annotation are considered non-view, +and cannot be called inside of `view` contexts, +like inside of a `view` function or in a precondition or postcondition. + +Function types can also have `view` annotations, +to be placed after the opening parenthesis but before the parameter list. +So, for example, these are valid types: + +```cadence + let f: view fun (Int): Int = ... + let h: view fun (): (view fun (): Void) = ... +``` + +Any function types without a `view` annotation will be considered non-view. + +Functions are covariant with respect to `view` annotations, +so a `view` function is a subtype of an non-view function with the same parameters and return types. +So, the following declarations would typecheck: + +```cadence + let a: view fun (): Void = view fun() {} + let b: fun (): Void = view fun() {} + let c: fun (): Void = fun() {} + let d: fun(view fun(): Void): Void = fun (x: fun(): Void) {} // contravariance +``` + +while these would not: + + +```cadence + let x: view fun (): Void = fun() {} + let y: fun(fun(): Void): Void = fun(f: view fun(): Void) {} // contravariance +``` + +The operations that are not permitted in `view` contexts are: + +* Calling a non-view function (including any functions that modify account state or storage like `save` or `load`) +* Writing to or modifying any resources +* Writing to or modifying any references +* Indexed assignment or writes to any variables not statically knowable to have been defined in the current function's scope, +or to any resources or references + +So, for example, this code would be allowed: + + ```cadence + view fun foo(): Int { + let a: [Int] = [] + a[0] = 3 + return a.length + } + ``` + + while this would not: + + ```cadence + let a: [Int] = [] + view fun foo(): Int { + a[0] = 3 + return a.length + } + ``` + +A caveat to this is that in some cases a non-`view` function that only performs a mutation that would be allowed in a `view` context will be rejected as a limitation of the analysis. +In particular, users may encounter this with arrays or dictionaries, where a function like: + +```cadence +view fun foo(): Int { + let a: [Int] = [0] + a[0] = 1 +} +``` + +is acceptable, because `a` is local to this function, while + +```cadence +view fun foo(): Int { + let a: [Int] = [0] + a.append(1) +} +``` + +will be rejected, because `append` is not `view`. + ## Function Calls Functions can be called (invoked). Function calls @@ -257,7 +354,7 @@ followed by a colon (`:`), and end with the return type. The whole function type needs to be enclosed in parentheses. ```cadence -// Declare a function named `add`, with the function type `((Int, Int): Int)`. +// Declare a function named `add`, with the function type `fun(Int, Int): Int`. // fun add(a: Int, b: Int): Int { return a + b @@ -265,9 +362,9 @@ fun add(a: Int, b: Int): Int { ``` ```cadence -// Declare a constant named `add`, with the function type `((Int, Int): Int)` +// Declare a constant named `add`, with the function type `fun(Int, Int): Int` // -let add: ((Int, Int): Int) = +let add: fun(Int, Int): Int = fun (a: Int, b: Int): Int { return a + b } @@ -279,17 +376,17 @@ If the function has no return type, it implicitly has the return type `Void`. // Declare a constant named `doNothing`, which is a function // that takes no parameters and returns nothing. // -let doNothing: ((): Void) = +let doNothing: fun(): Void = fun () {} ``` Parentheses also control precedence. -For example, a function type `((Int): ((): Int))` is the type +For example, a function type `fun(Int): fun(): Int` is the type for a function which accepts one argument with type `Int`, and which returns another function, that takes no arguments and returns an `Int`. -The type `[((Int): Int); 2]` specifies an array type of two functions, +The type `[fun(Int): Int; 2]` specifies an array type of two functions, which accept one integer and return one integer. Argument labels are not part of the function type. @@ -301,7 +398,7 @@ cannot accept argument labels. ```cadence // Declare a function which takes one argument that has type `Int`. -// The function has type `((Int): Void)`. +// The function has type `fun(Int): Void`. // fun foo1(x: Int) {} @@ -309,17 +406,17 @@ fun foo1(x: Int) {} foo1(x: 1) // Declare another function which takes one argument that has type `Int`. -// The function also has type `((Int): Void)`. +// The function also has type `fun(Int): Void`. // fun foo2(y: Int) {} // Call function `foo2`. This requires an argument label. foo2(y: 2) -// Declare a variable which has type `((Int): Void)` and use `foo1` +// Declare a variable which has type `fun(Int): Void` and use `foo1` // as its initial value. // -var someFoo: ((Int): Void) = foo1 +var someFoo: fun(Int): Void = foo1 // Call the function assigned to variable `someFoo`. // This is valid as the function types match. @@ -351,7 +448,7 @@ and assign to the variables it refers to. // Declare a function named `makeCounter` which returns a function that // each time when called, returns the next integer, starting at 1. // -fun makeCounter(): ((): Int) { +fun makeCounter(): fun(): Int { var count = 0 return fun (): Int { // NOTE: read from and assign to the non-local variable @@ -480,6 +577,10 @@ fun incrementN() { } ``` +Both preconditions and postconditions are considered [`view` contexts](#view-functions); +any operations that are not legal in functions with `view` annotations are also not allowed in conditions. +In particular, this means that if you wish to call a function in a condition, that function must be `view`. + ## Functions are Values Functions are values ("first-class"), so they may be assigned to variables and fields @@ -490,7 +591,7 @@ or passed to functions as arguments. // Declare a function named `transform` which applies a function to each element // of an array of integers and returns a new array of the results. // -pub fun transform(function: ((Int): Int), integers: [Int]): [Int] { +access(all) fun transform(function: fun(Int): Int, integers: [Int]): [Int] { var newIntegers: [Int] = [] for integer in integers { newIntegers.append(function(integer)) @@ -498,7 +599,7 @@ pub fun transform(function: ((Int): Int), integers: [Int]): [Int] { return newIntegers } -pub fun double(_ integer: Int): Int { +access(all) fun double(_ integer: Int): Int { return integer * 2 } diff --git a/versioned_docs/version-current_0.42/0.42/language/glossary.mdx b/versioned_docs/version-1.0/language/glossary.mdx similarity index 92% rename from versioned_docs/version-current_0.42/0.42/language/glossary.mdx rename to versioned_docs/version-1.0/language/glossary.mdx index 817ccd1..92b3eae 100644 --- a/versioned_docs/version-current_0.42/0.42/language/glossary.mdx +++ b/versioned_docs/version-1.0/language/glossary.mdx @@ -14,7 +14,7 @@ The `&` (ampersand) symbol has several uses. ### Reference -If an expression starts with the `&` (ampersand) symbol, it creates a [reference](./references.md). +If an expression starts with the `&` (ampersand) symbol, it creates a [reference](./references.mdx). ```cadence let a: String = "hello" @@ -23,12 +23,12 @@ let refOfA: &String = &a as &String References may also be authorized if the `&` symbol is preceded by `auth` (otherwise the reference is unauthorized). -Authorized references have the `auth` modifier, i.e. the full syntax is `auth &T`, -whereas unauthorized references do not have a modifier. +Authorized references have the `auth` modifier, along with the set of entitlements to which the reference is authorized, +i.e. the full syntax is `auth(E, F) &T`, whereas unauthorized references do not have a modifier. ```cadence let a: String = "hello" -let refOfA: &String = &a as auth &String +let refOfA: auth(X) &String = &a as auth(X) &String ``` ### Logical Operator @@ -53,8 +53,8 @@ This emphasizes the whole type acts like a resource. ```cadence // Declare a resource named `SomeResource` -pub resource SomeResource { - pub var value: Int +access(all) resource SomeResource { + access(all) var value: Int init(value: Int) { self.value = value @@ -65,7 +65,7 @@ pub resource SomeResource { let a: @SomeResource <- create SomeResource(value: 0) // also in functions declarations -pub fun use(resource: @SomeResource) { +access(all) fun use(resource: @SomeResource) { destroy resource } ``` @@ -156,7 +156,7 @@ let result = 4 / 2 ### Path separator -In a [Path](./accounts.mdx#paths), the forward slash separates the domain (e.g. `storage`, `private`, `public`) and the identifier. +In a [path](./accounts/paths.mdx), the forward slash separates the domain, `storage` or `public`, and the identifier. ```cadence let storagePath = /storage/path @@ -187,7 +187,7 @@ If the variable is `nil`, the move succeeds. If it is not nil, the program aborts. ```cadence -pub resource R {} +access(all) resource R {} var a: @R? <- nil a <-! create R() diff --git a/versioned_docs/version-current_0.42/0.42/language/imports.mdx b/versioned_docs/version-1.0/language/imports.mdx similarity index 91% rename from versioned_docs/version-current_0.42/0.42/language/imports.mdx rename to versioned_docs/version-1.0/language/imports.mdx index 3bc725a..287c9d1 100644 --- a/versioned_docs/version-current_0.42/0.42/language/imports.mdx +++ b/versioned_docs/version-1.0/language/imports.mdx @@ -1,6 +1,6 @@ --- title: Imports -sidebar_position: 18 +sidebar_position: 19 --- Programs can import declarations (types, functions, variables, etc.) from other programs. @@ -12,7 +12,7 @@ or it can be followed by the names of the declarations that should be imported, followed by the `from` keyword, and then followed by the location. If importing a local file, the location is a string literal, and the path to the file. -Deployment of code with file imports requires the usage for the [Flow CLI](https://developers.flow.com/tools/flow-cli/). +Deployment of code with file imports requires the usage for the [Flow CLI](https://developers.flow.com/tools/flow-cli/index.md). If importing an external type in a different account, the location is an address literal, and the address diff --git a/versioned_docs/version-current_0.42/0.42/language/index.md b/versioned_docs/version-1.0/language/index.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/index.md rename to versioned_docs/version-1.0/language/index.md diff --git a/versioned_docs/version-current_0.42/0.42/language/interfaces.mdx b/versioned_docs/version-1.0/language/interfaces.mdx similarity index 52% rename from versioned_docs/version-current_0.42/0.42/language/interfaces.mdx rename to versioned_docs/version-1.0/language/interfaces.mdx index b1704ae..efacfb6 100644 --- a/versioned_docs/version-current_0.42/0.42/language/interfaces.mdx +++ b/versioned_docs/version-1.0/language/interfaces.mdx @@ -1,6 +1,6 @@ --- title: Interfaces -sidebar_position: 14 +sidebar_position: 15 --- An interface is an abstract type that specifies the behavior of types @@ -60,9 +60,7 @@ or the field requirement may specify nothing, in which case the implementation may either be a variable or a constant field. Field requirements and function requirements must specify the required level of access. -The access must be at least be public, so the `pub` keyword must be provided. -Variable field requirements can be specified to also be publicly settable -by using the `pub(set)` keyword. +The access must be at least be public, so the `access(all)` keyword must be provided. Interfaces can be used in types. This is explained in detail in the section [Interfaces in Types](#interfaces-in-types). @@ -72,16 +70,16 @@ For now, the syntax `{I}` can be read as the type of any value that implements t // Declare a resource interface for a fungible token. // Only resources can implement this resource interface. // -pub resource interface FungibleToken { +access(all) resource interface FungibleToken { // Require the implementing type to provide a field for the balance - // that is readable in all scopes (`pub`). + // that is readable in all scopes (`access(all)`). // // Neither the `var` keyword, nor the `let` keyword is used, // so the field may be implemented as either a variable // or as a constant field. // - pub balance: Int + access(all) balance: Int // Require the implementing type to provide an initializer that // given the initial balance, must initialize the balance field. @@ -111,7 +109,7 @@ pub resource interface FungibleToken { // The type `{FungibleToken}` is the type of any resource // that implements the resource interface `FungibleToken`. // - pub fun withdraw(amount: Int): @{FungibleToken} { + access(all) fun withdraw(amount: Int): @{FungibleToken} { pre { amount > 0: "the amount must be positive" @@ -137,7 +135,7 @@ pub resource interface FungibleToken { // The parameter type `{FungibleToken}` is the type of any resource // that implements the resource interface `FungibleToken`. // - pub fun deposit(_ token: @{FungibleToken}) { + access(all) fun deposit(_ token: @{FungibleToken}) { post { self.balance == before(self.balance) + token.balance: "the amount must be added to the balance" @@ -184,7 +182,7 @@ in terms of name, parameter argument labels, parameter types, and the return typ // It has a variable field named `balance`, that can be written // by functions of the type, but outer scopes can only read it. // -pub resource ExampleToken: FungibleToken { +access(all) resource ExampleToken: FungibleToken { // Implement the required field `balance` for the `FungibleToken` interface. // The interface does not specify if the field must be variable, constant, @@ -192,7 +190,7 @@ pub resource ExampleToken: FungibleToken { // but limit outer scopes to only read from the field, it is declared variable, // and only has public access (non-settable). // - pub var balance: Int + access(all) var balance: Int // Implement the required initializer for the `FungibleToken` interface: // accept an initial balance and initialize the `balance` field. @@ -216,7 +214,7 @@ pub resource ExampleToken: FungibleToken { // NOTE: neither the precondition nor the postcondition declared // in the interface have to be repeated here in the implementation. // - pub fun withdraw(amount: Int): @ExampleToken { + access(all) fun withdraw(amount: Int): @ExampleToken { self.balance = self.balance - amount return create ExampleToken(balance: amount) } @@ -237,7 +235,7 @@ pub resource ExampleToken: FungibleToken { // NOTE: neither the precondition nor the postcondition declared // in the interface have to be repeated here in the implementation. // - pub fun deposit(_ token: @{FungibleToken}) { + access(all) fun deposit(_ token: @{FungibleToken}) { if let exampleToken <- token as? ExampleToken { self.balance = self.balance + exampleToken.balance destroy exampleToken @@ -287,26 +285,24 @@ token.withdraw(amount: 90) The access level for variable fields in an implementation may be less restrictive than the interface requires. For example, an interface may require a field to be -at least public (i.e. the `pub` keyword is specified), +at least contract-accessible (i.e. the `access(contract)` modifier is used), and an implementation may provide a variable field which is public, -but also publicly settable (the `pub(set)` keyword is specified). +(the `access(all)` modifier is used). ```cadence -pub struct interface AnInterface { - // Require the implementing type to provide a publicly readable +access(all) struct interface AnInterface { + // Require the implementing type to provide a contract-readable // field named `a` that has type `Int`. It may be a variable // or a constant field. // - pub a: Int + access(contract) a: Int } -pub struct AnImplementation: AnInterface { - // Declare a publicly settable variable field named `a` that has type `Int`. +access(all) struct AnImplementation: AnInterface { + // Declare a public variable field named `a` that has type `Int`. // This implementation satisfies the requirement for interface `AnInterface`: - // The field is at least publicly readable, but this implementation also - // allows the field to be written to in all scopes. // - pub(set) var a: Int + access(all) var a: Int init(a: Int) { self.a = a @@ -319,7 +315,7 @@ pub struct AnImplementation: AnInterface { Interfaces can be used in types: The type `{I}` is the type of all objects that implement the interface `I`. -This is called a [restricted type](./restricted-types.md): +This is called a [intersection type](./intersection-types.md): Only the functionality (members and functions) of the interface can be used when accessing a value of such a type. @@ -329,18 +325,18 @@ when accessing a value of such a type. // Require implementing types to provide a field which returns the area, // and a function which scales the shape by a given factor. // -pub struct interface Shape { - pub fun getArea(): Int - pub fun scale(factor: Int) +access(all) struct interface Shape { + access(all) fun getArea(): Int + access(all) fun scale(factor: Int) } // Declare a structure named `Square` the implements the `Shape` interface. // -pub struct Square: Shape { +access(all) struct Square: Shape { // In addition to the required fields from the interface, // the type can also declare additional fields. // - pub var length: Int + access(all) var length: Int // Provided the field `area` which is required to conform // to the interface `Shape`. @@ -348,36 +344,36 @@ pub struct Square: Shape { // Since `area` was not declared as a constant, variable, // field in the interface, it can be declared. // - pub fun getArea(): Int { + access(all) fun getArea(): Int { return self.length * self.length } - pub init(length: Int) { + access(all) init(length: Int) { self.length = length } // Provided the implementation of the function `scale` // which is required to conform to the interface `Shape`. // - pub fun scale(factor: Int) { + access(all) fun scale(factor: Int) { self.length = self.length * factor } } // Declare a structure named `Rectangle` that also implements the `Shape` interface. // -pub struct Rectangle: Shape { - pub var width: Int - pub var height: Int +access(all) struct Rectangle: Shape { + access(all) var width: Int + access(all) var height: Int // Provided the field `area which is required to conform // to the interface `Shape`. // - pub fun getArea(): Int { + access(all) fun getArea(): Int { return self.width * self.height } - pub init(width: Int, height: Int) { + access(all) init(width: Int, height: Int) { self.width = width self.height = height } @@ -385,7 +381,7 @@ pub struct Rectangle: Shape { // Provided the implementation of the function `scale` // which is required to conform to the interface `Shape`. // - pub fun scale(factor: Int) { + access(all) fun scale(factor: Int) { self.width = self.width * factor self.height = self.height * factor } @@ -469,6 +465,40 @@ struct SomeInner: OuterInterface.InnerInterface {} ``` +Contract interfaces may also declare [events](./events.md), +which also do not require implementing types of the outer interface to "implement" the event. +The event can be emitted in the declaring interface, in a condition or in a default implementation of a function. E.g. + +```cadence +// Declare a contract interface +// +contract interface ContractInterface { + // some event declaration + // + event SomeEvent() + + // some function that emits `SomeEvent` when called + // + fun eventEmittingFunction() { + pre { + emit SomeEvent() + } + } +} + +// A contract implementing `ContractInterface` +// Note that no declaration of `SomeEvent` is required +// +contract ImplementingContract: ContractInterface { + // implementation of `eventEmittingFunction`; + // this will emit `SomeEvent` when called + // + fun eventEmittingFunction() { + // ... + } +} +``` + ## Interface Default Functions Interfaces can provide default functions: @@ -510,38 +540,411 @@ Interfaces cannot provide default initializers or default destructors. Only one conformance may provide a default function. -## Nested Type Requirements +## Interface inheritance - +An interface can inherit from (conform to) other interfaces of the same kind. +For example, a resource interface can inherit from another resource interface, but cannot inherit from a struct interface. +When an interface inherits from another, all the fields, functions, and types of the parent interface are implicitly +available to the inheriting interface. -🚧 Status: Currently only contracts and contract interfaces support nested type requirements. +```cadence +access(all) resource interface Receiver { + access(all) fun deposit(_ something: @AnyResource) +} - +// `Vault` interface inherits from `Receiver` interface. +access(all) resource interface Vault: Receiver { + access(all) fun withdraw(_ amount: Int): @Vault +} +``` -Interfaces can require implementing types to provide concrete nested types. -For example, a resource interface may require an implementing type to provide a resource type. +In the example above, `Vault` inherits `Receiver`. Anyone implementing the `Vault` interface would also have to +implement the `Receiver` interface. ```cadence -// Declare a resource interface named `FungibleToken`. -// -// Require implementing types to provide a resource type named `Vault` -// which must have a field named `balance`. -// -resource interface FungibleToken { - pub resource Vault { - pub balance: Int +access(all) resource MyVault: Vault { + // Must implement all the methods coming from both `Vault` and `Receiver` interfaces. + access(all) fun deposit(_ something: @AnyResource) {} + + access(all) fun withdraw(_ amount: Int): @Vault {} +} +``` + +### Duplicate interface members + +When an interface implements another interface, it is possible for the two interfaces to have members with the same name. +The following sections explain how these ambiguities are resolved for different scenarios. + +#### Fields + +If two fields with identical names have identical types, then it will be valid. + +```cadence +access(all) resource interface Receiver { + access(all) var id: UInt64 +} + +access(all) resource interface Vault: Receiver { + // `id` field has the same type as the `Receiver.id`. Hence this is valid. + access(all) var id: UInt64 +} +``` + +Otherwise, interface conformance is not valid. + +```cadence +access(all) resource interface Receiver { + access(all) var id: Int +} + +access(all) resource interface Vault: Receiver { + // `id` field has a different type than the `Receiver.id`. Hence this is invalid. + access(all) var id: UInt64 +} +``` + +#### Functions + +If two functions with identical names also have identical signatures, that is valid. + +```cadence +access(all) resource interface Receiver { + access(all) fun deposit(_ something: @AnyResource) +} + +access(all) resource interface Vault: Receiver { + // `deposit` function has the same signature as the `Receiver.deposit`. + // Also none of them have any default implementations. + // Hence this is valid. + access(all) fun deposit(_ something: @AnyResource) +} +``` + +If the signatures of the two functions are different, then the interface conformance is not valid. + +```cadence +access(all) resource interface Receiver { + access(all) fun deposit(_ something: @AnyResource) +} + +access(all) resource interface Vault: Receiver { + // Error: `deposit` function has a different signature compared to the `Receiver.deposit`. + // So these two cannot co-exist. + access(all) fun deposit() +} +``` + +#### Functions with conditions + +If the two functions with identical names and signatures have pre/post conditions, then it will still be valid. +However, the pre/post conditions are linearized (refer to the [linearizing conditions section](#linearizing-conditions)) +to determine the order of the execution of the conditions. +Given the pre/post conditions are `view` only, the order of execution would not have an impact on the conditions. + +```cadence +access(all) resource interface Receiver { + access(all) fun deposit(_ something: @AnyResource) { + pre{ self.balance > 100 } } } -// Declare a resource named `ExampleToken` that implements the `FungibleToken` interface. -// -// The nested type `Vault` must be provided to conform to the interface. -// -resource ExampleToken: FungibleToken { - pub resource Vault { - pub var balance: Int - init(balance: Int) { - self.balance = balance - } + +access(all) resource interface Vault: Receiver { + // `deposit` function has the same signature as the `Receiver.deposit`. + // Having pre/post condition is valid. + // Both conditions would be executed, in a pre-determined order. + access(all) fun deposit(_ something: @AnyResource) { + pre{ self.balance > 50 } + } +} +``` + +#### Default functions + +An interface can provide a default implementation to an inherited function. + +```cadence +access(all) resource interface Receiver { + access(all) fun log(_ message: String) +} + +access(all) resource interface Vault: Receiver { + // Valid: Provides the implementation for `Receiver.log` method. + access(all) fun log(_ message: String) { + log(message.append("from Vault")) + } +} +``` + +However, an interface cannot override an inherited default implementation of a function. + +```cadence +access(all) resource interface Receiver { + access(all) fun log(_ message: String) { + log(message.append("from Receiver")) + } +} + +access(all) resource interface Vault: Receiver { + // Invalid: Cannot override the `Receiver.log` method. + access(all) fun log(_ message: String) { + log(message.append("from Vault")) + } +} +``` + +It is also invalid to have two or more inherited default implementations for an interface. + +```cadence +access(all) resource interface Receiver { + access(all) fun log(_ message: String) { + log(message.append("from Receiver")) + } +} + +access(all) resource interface Provider { + access(all) fun log(_ message: String) { + log(message.append("from Provider")) + } +} + +// Invalid: Two default functions from two interfaces. +access(all) resource interface Vault: Receiver, Provider {} +``` + +Having said that, there can be situations where the same default function can be available via different +inheritance paths. + +```cadence +access(all) resource interface Logger { + access(all) fun log(_ message: String) { + log(message.append("from Logger")) } } + +access(all) resource interface Receiver: Logger {} + +access(all) resource interface Provider: Logger {} + +// Valid: `Logger.log()` default function is visible to the `Vault` interface +// via both `Receiver` and `Provider`. +access(all) resource interface Vault: Receiver, Provider {} +``` + +In the above example, `Logger.log()` default function is visible to the `Vault` interface via both `Receiver` and `Provider`. +Even though it is available from two different interfaces, they are both referring to the same +default implementation. +Therefore, the above code is valid. + +#### Conditions with Default functions + +A more complex situation is where a default function is available via one inheritance path and a pre/post condition +is available via another inheritance path. + +```cadence +access(all) resource interface Receiver { + access(all) fun log(_ message: String) { + log(message.append("from Receiver")) + } +} + +access(all) resource interface Provider { + access(all) fun log(_ message: String) { + pre{ message != "" } + } +} + +// Valid: Both the default function and the condition would be available. +access(all) resource interface Vault: Receiver, Provider {} +``` + +In such situations, all rules applicable for default functions inheritance as well as condition inheritance +would be applied. +Thus, the default function from coming from the `Receiver` interface, and the condition comes from the `Provider` +interface would be made available for the inherited interface. + +#### Types and event definitions + +Type and event definitions would also behave similarly to the default functions. +Inherited interfaces can override type definitions and event definitions. + +```cadence +access(all) contract interface Token { + access(all) struct Foo {} +} + +access(all) contract interface NonFungibleToken: Token { + access(all) struct Foo {} +} + +access(all) contract MyToken: NonFungibleToken { + access(all) fun test() { + let foo: Foo // This will refer to the `NonFungibleToken.Foo` + } +} +``` + +If a user needed to access the `Foo` struct coming from the super interface `Token`, then they can +access it using the fully qualified name. e.g: `let foo: Token.Foo`. + +However, it is not allowed to have two or more inherited type/events definitions with identical names for an interface. + +```cadence +access(all) contract interface Token { + access(all) struct Foo {} +} + +access(all) contract interface Collectible { + access(all) struct Foo {} +} + +// Invalid: Two type definitions with the same name from two interfaces. +access(all) contract NonFungibleToken: Token, Collectible { +} +``` +Similar to default functions, there can be situations where the same type/event definition can be available +via different inheritance paths. + +```cadence +access(all) contract interface Logger { + access(all) struct Foo {} +} + +access(all) contract interface Token: Logger {} + +access(all) contract interface Collectible: Logger {} + +// Valid: `Logger.Foo` struct is visible to the `NonFungibleToken` interface via both `Token` and `Collectible`. +access(all) contract interface NonFungibleToken: Token, Collectible {} +``` + +In the above example, `Logger.Foo` type definition is visible to the `NonFungibleToken` interface via both `Token` +and `Collectible`. +Even though it is available from two different interfaces, they are both referring to the same +type definition. +Therefore, the above code is valid. + +However, if at least one of the interfaces in the middle of the chain also overrides the type definition `Foo`, +then the code becomes invalid, as there are multiple implementations present now, which leads to ambiguity. + +```cadence +access(all) contract interface Logger { + access(all) struct Foo {} +} + +access(all) contract interface Token: Logger { + access(all) struct Foo {} +} + +access(all) contract interface Collectible: Logger {} + +// Invalid: The default implementation of the `Foo` struct by the `Logger` +// interface is visible to the `NonFungibleToken` via the `Collectible` interface. +// Another implementation of `Foo` struct is visible to the `NonFungibleToken` via the `Token` interface. +// This creates ambiguity. +access(all) resource interface NonFungibleToken: Token, Provider {} +``` + + +### Linearizing Conditions + +As mentioned in the [functions with conditions](#functions-with-conditions) section, it would be required to linearize +the function conditions, to determine the order in which pre- and post-conditions are executed. +This is done by linearizing the interfaces, and hence conditions, in a **depth-first pre-ordered manner, without duplicates**. + +For example, consider an interface inheritance hierarchy as below: +``` + A + / \ + B C + / \ / + D E +where an edge from A (top) to B (bottom) means A inherits B. +``` + +This would convert to a Cadence implementation similar to: + +```cadence +struct interface A: B, C { + access(all) fun test() { + pre { print("A") } + } +} + +struct interface B: D, E { + access(all) fun test() { + pre { print("B") } + } +} + +struct interface C: E { + access(all) fun test() { + pre { print("C") } + } +} + +struct interface D { + access(all) fun test() { + pre { print("D") } + } +} + +struct interface E { + access(all) fun test() { + pre { print("E") } + } +} +``` + +Any concrete type implementing interface `A` would be equivalent to implementing all interfaces from `A` to `E`, linearized. + +```cadence +struct Foo: A { + access(all) fun test() { + pre { print("Foo") } + } +} +``` + +The linearized interface order would be: [A, B, D, E, C]. + +i.e: same as having: +```cadence +struct Foo: A, B, D, C, E { + access(all) fun test() { + pre { print("Foo") } + } +} +``` + +Thus, invoking `test` method of `Foo` would first invoke the pre-conditions of [A, B, D, E, C], in that particular order, +and eventually runs the pre-condition of the concrete implementation `Foo`. + +```cadence +let foo = Foo() +foo.test() +``` + +Above will print: + +``` +A +B +D +E +C +Foo +``` + +Similarly, for post-conditions, the same linearization of interfaces would be used, and the post-conditions are executed +in the reverse order. +For example, replacing the `pre` conditions in the above example with `post` conditions with the exact same content would +result in an output similar to: + +``` +Foo +C +E +D +B +A ``` diff --git a/versioned_docs/version-current_0.42/0.42/language/intersection-types.md b/versioned_docs/version-1.0/language/intersection-types.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/intersection-types.md rename to versioned_docs/version-1.0/language/intersection-types.md diff --git a/versioned_docs/version-current_0.42/0.42/language/operators.md b/versioned_docs/version-1.0/language/operators.md similarity index 99% rename from versioned_docs/version-current_0.42/0.42/language/operators.md rename to versioned_docs/version-1.0/language/operators.md index 7def4bc..3acf814 100644 --- a/versioned_docs/version-current_0.42/0.42/language/operators.md +++ b/versioned_docs/version-1.0/language/operators.md @@ -623,6 +623,7 @@ They're often used in low-level programming. For unsigned integers, the bitwise shifting operators perform [logical shifting](https://en.wikipedia.org/wiki/Logical_shift), for signed integers, they perform [arithmetic shifting](https://en.wikipedia.org/wiki/Arithmetic_shift). +Also note that for `a << b` or `a >> b`, `b` must fit into a 64-bit integer. ## Ternary Conditional Operator diff --git a/docs/language/references.mdx b/versioned_docs/version-1.0/language/references.mdx similarity index 100% rename from docs/language/references.mdx rename to versioned_docs/version-1.0/language/references.mdx diff --git a/versioned_docs/version-current_0.42/0.42/language/resources.mdx b/versioned_docs/version-1.0/language/resources.mdx similarity index 95% rename from versioned_docs/version-current_0.42/0.42/language/resources.mdx rename to versioned_docs/version-1.0/language/resources.mdx index 17e2dda..645f28b 100644 --- a/versioned_docs/version-current_0.42/0.42/language/resources.mdx +++ b/versioned_docs/version-1.0/language/resources.mdx @@ -40,8 +40,8 @@ and when it is returned from a function. ```cadence // Declare a resource named `SomeResource`, with a variable integer field. // -pub resource SomeResource { - pub var value: Int +access(all) resource SomeResource { + access(all) var value: Int init(value: Int) { self.value = value @@ -69,7 +69,7 @@ b.value // equals 0 // // The parameter has a resource type, so the type annotation must be prefixed with `@`. // -pub fun use(resource: @SomeResource) { +access(all) fun use(resource: @SomeResource) { // ... } @@ -128,7 +128,7 @@ let someResource: @SomeResource <- create SomeResource(value: 5) // // The parameter has a resource type, so the type annotation must be prefixed with `@`. // -pub fun use(resource: @SomeResource) { +access(all) fun use(resource: @SomeResource) { destroy resource } @@ -137,7 +137,7 @@ pub fun use(resource: @SomeResource) { // The return type is a resource type, so the type annotation must be prefixed with `@`. // The return statement must also use the `<-` operator to make it explicit the resource is moved. // -pub fun get(): @SomeResource { +access(all) fun get(): @SomeResource { let newResource <- create SomeResource() return <-newResource } @@ -149,7 +149,7 @@ Resources **must** be used exactly once. // Declare a function which consumes a resource but does not use it. // This function is invalid, because it would cause a loss of the resource. // -pub fun forgetToUse(resource: @SomeResource) { +access(all) fun forgetToUse(resource: @SomeResource) { // Invalid: The resource parameter `resource` is not used, but must be. } ``` @@ -177,7 +177,7 @@ res.value // This function is invalid, because it does not always use the resource parameter, // which would cause a loss of the resource. // -pub fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) { +access(all) fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) { if destroyResource { destroy resource } @@ -192,7 +192,7 @@ pub fun sometimesDestroy(resource: @SomeResource, destroyResource: Bool) { // This function is valid, as it always uses the resource parameter, // and does not cause a loss of the resource. // -pub fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { +access(all) fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { if destroyResource { destroy resource } else { @@ -208,7 +208,7 @@ pub fun alwaysUse(resource: @SomeResource, destroyResource: Bool) { // This function is invalid, because it does not always use the resource parameter, // which would cause a loss of the resource. // -pub fun returnBeforeDestroy(move: Bool) { +access(all) fun returnBeforeDestroy(move: Bool) { let res <- create SomeResource(value: 1) if move { use(resource: <-res) @@ -234,7 +234,7 @@ Instead, use a swap statement (`<->`) or shift statement (`<- target <-`) to replace the resource variable with another resource. ```cadence -pub resource R {} +access(all) resource R {} var x <- create R() var y <- create R() @@ -268,7 +268,7 @@ A resource may have only one destructor. ```cadence var destructorCalled = false -pub resource Resource { +access(all) resource Resource { // Declare a destructor for the resource, which is executed // when the resource is destroyed. @@ -292,7 +292,7 @@ it **must** declare a destructor, which **must** invalidate all resource fields, i.e. move or destroy them. ```cadence -pub resource Child { +access(all) resource Child { let name: String init(name: String) @@ -304,7 +304,7 @@ pub resource Child { // The resource *must* declare a destructor // and the destructor *must* invalidate the resource field. // -pub resource Parent { +access(all) resource Parent { let name: String var child: @Child @@ -355,7 +355,7 @@ resource R {} // the resource parameter `resource`. Each call to the returned function // would return the resource, which should not be possible. // -fun makeCloner(resource: @R): ((): @R) { +fun makeCloner(resource: @R): fun(): @R { return fun (): @R { return <-resource } @@ -599,8 +599,8 @@ Do not rely on or assume any particular behaviour in Cadence programs. ## Resource Owner -Resources have the implicit field `let owner: PublicAccount?`. -If the resource is currently [stored in an account](./accounts.mdx#account-storage), +Resources have the implicit field `let owner: &Account?`. +If the resource is currently [stored in an account](./accounts/storage.mdx), then the field contains the publicly accessible portion of the account. Otherwise the field is `nil`. diff --git a/versioned_docs/version-current_0.42/0.42/language/run-time-types.md b/versioned_docs/version-1.0/language/run-time-types.md similarity index 90% rename from versioned_docs/version-current_0.42/0.42/language/run-time-types.md rename to versioned_docs/version-1.0/language/run-time-types.md index 81da987..5ede0b0 100644 --- a/versioned_docs/version-current_0.42/0.42/language/run-time-types.md +++ b/versioned_docs/version-1.0/language/run-time-types.md @@ -91,26 +91,25 @@ Run-time types can also be constructed from type identifier strings using built- ```cadence fun CompositeType(_ identifier: String): Type? fun InterfaceType(_ identifier: String): Type? -fun RestrictedType(identifier: String?, restrictions: [String]): Type? +fun IntersectionType(types: [String]): Type? ``` -Given a type identifier (as well as a list of identifiers for restricting interfaces -in the case of `RestrictedType`), these functions will look up nominal types and +Given a type identifier (or a list of identifiers for interfaces +in the case of `IntersectionType`), these functions will look up nominal types and produce their run-time equivalents. If the provided identifiers do not correspond -to any types, or (in the case of `RestrictedType`) the provided combination of +to any types, or (in the case of `IntersectionType`) the provided combination of identifiers would not type-check statically, these functions will produce `nil`. ```cadence -struct Test {} +struct Test: I {} struct interface I {} let type: Type = CompositeType("A.0000000000000001.Test") // `type` is `Type` -let type2: Type = RestrictedType( - identifier: type.identifier, +let type2: Type = IntersectionType( restrictions: ["A.0000000000000001.I"] ) -// `type2` is `Type` +// `type2` is `Type<{I}>` ``` Other built-in functions will construct compound types from other run-types. @@ -124,7 +123,7 @@ fun FunctionType(parameters: [Type], return: Type): Type fun DictionaryType(key: Type, value: Type): Type? // returns `nil` if `type` is not a reference type fun CapabilityType(_ type: Type): Type? -fun ReferenceType(authorized: bool, type: Type): Type +fun ReferenceType(entitlements: [String], type: Type): Type? ``` ### Asserting the Type of a Value @@ -181,21 +180,21 @@ something.isInstance(Type()) // is `false` For example, this allows implementing a marketplace sale resource: ```cadence -pub resource SimpleSale { +access(all) resource SimpleSale { /// The resource for sale. /// Once the resource is sold, the field becomes `nil`. /// - pub var resourceForSale: @AnyResource? + access(all) var resourceForSale: @AnyResource? /// The price that is wanted for the purchase of the resource. /// - pub let priceForResource: UFix64 + access(all) let priceForResource: UFix64 /// The type of currency that is required for the purchase. /// - pub let requiredCurrency: Type - pub let paymentReceiver: Capability<&{FungibleToken.Receiver}> + access(all) let requiredCurrency: Type + access(all) let paymentReceiver: Capability<&{FungibleToken.Receiver}> /// `paymentReceiver` is the capability that will be borrowed /// once a valid purchase is made. @@ -226,7 +225,7 @@ pub resource SimpleSale { /// If the purchase succeeds, the resource for sale is returned. /// If the purchase fails, the program aborts. /// - pub fun buyObject(with funds: @FungibleToken.Vault): @AnyResource { + access(all) fun buyObject(with funds: @FungibleToken.Vault): @AnyResource { pre { // Ensure the resource is still up for sale self.resourceForSale != nil: "The resource has already been sold" diff --git a/versioned_docs/version-current_0.42/0.42/language/scope.md b/versioned_docs/version-1.0/language/scope.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/scope.md rename to versioned_docs/version-1.0/language/scope.md diff --git a/versioned_docs/version-current_0.42/0.42/language/syntax.md b/versioned_docs/version-1.0/language/syntax.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/syntax.md rename to versioned_docs/version-1.0/language/syntax.md diff --git a/versioned_docs/version-1.0/language/transactions.md b/versioned_docs/version-1.0/language/transactions.md new file mode 100644 index 0000000..dda8d2f --- /dev/null +++ b/versioned_docs/version-1.0/language/transactions.md @@ -0,0 +1,246 @@ +--- +title: Transactions +sidebar_position: 24 +--- + +Transactions are objects that are signed with keys of one or more [accounts](./accounts/index.mdx) +and are sent to the chain to interact with it and perform state changes. + +Transaction can [import](./imports.mdx) any number of types from any account using the import syntax. + +```cadence +import FungibleToken from 0x01 +``` + +A transaction is declared using the `transaction` keyword +and its contents are contained in curly braces. + +The body of the transaction can declare local variables +that are valid throughout the whole of the transaction. + +```cadence +transaction { + // transaction contents + let localVar: Int + + // ... +} +``` + +## Transaction parameters + +Transactions can have parameters. +Transaction parameters are declared like function parameters. +The arguments for the transaction are passed along with the transaction. + +Transaction parameters are accessible throughout the whole of the transaction. + +```cadence +// Declare a transaction which has one parameter named `amount` +// that has the type `UFix64` +// +transaction(amount: UFix64) { + +} +``` + +## Transaction phases + +Transactions are executed in four phases: +preparation, pre-conditions, execution, and post-conditions, in that order. +The preparation and execution phases are blocks of code that execute sequentially. +The pre-conditions and post-condition are just like +[conditions in functions](./functions.mdx#function-preconditions-and-postconditions). + +The following empty Cadence transaction has no logic, +but demonstrates the syntax for each phase, in the order these phases are executed: + +```cadence +transaction { + prepare(signer1: &Account, signer2: &Account) { + // ... + } + + pre { + // ... + } + + execute { + // ... + } + + post { + // ... + } +} +``` + +Although optional, each phase serves a specific purpose when executing a transaction +and it is recommended that developers use these phases when creating their transactions. + +### Prepare phase + +The `prepare` phase is used when the transaction needs access +to the accounts which signed (authorized) the transaction. + +Access to the signing accounts is **only possible inside the `prepare` phase**. + +For each signer of the transaction, +a [reference](./references.mdx) to the signing account is passed as an argument to the `prepare` phase. +The reference may be authorized, requesting certain [access to the account](./accounts/index.mdx#account-access). + +For example, if the transaction has two signers, +the prepare **must** have two parameters of type `&Account`. + +```cadence +prepare(signer1: &Account, signer2: &Account) { + // ... +} +``` + +For instance, to request write access to an [account's storage](./accounts/storage.mdx), +the transaction can request an authorized reference: + +```cadence +prepare(signer: auth(Storage) &Account) { + // ... +} +``` + +As a best practice, only use the `prepare` phase to define and execute logic +that requires [write access](./accounts/index.mdx#performing-write-operations) to the signing accounts, +and *move all other logic elsewhere*. + +Modifications to accounts can have significant implications, +so keep this phase clear of unrelated logic. +This ensures that users can easily read and understand the logic of the transaction +and how it affects their account. + +The prepare phase serves a similar purpose as the +[initializer of a composite](https://developers.flow.com/next/cadence/language/composite-types#composite-type-fields). + +For example, if a transaction performs a token transfer, put the withdrawal in the `prepare` phase, +as it requires access to the account storage, but perform the deposit in the `execute` phase. + +### Pre-conditions + +Transaction pre-conditions are just like +[pre-conditions of functions](./functions.mdx#function-preconditions-and-postconditions). + +Pre-conditions are optional and are declared in a `pre` block. +They are executed after the `prepare` phase, +and are used for checking if explicit conditions hold before executing the remainder of the transaction. +The block can have zero or more conditions. + +For example, a pre-condition might check the balance before transferring tokens between accounts. + +```cadence +pre { + sendingAccount.balance > 0 +} +``` + +If any of the pre-conditions fail, +then the remainder of the transaction is not executed and it is completely reverted. + +### Execute phase + +The `execute` block executes the main logic of the transaction. +This phase is optional, but it is a best practice to add your main transaction logic in the section, +so it is explicit. + +It is impossible to access the references to the transaction's signing accounts in the `execute` phase. + +```cadence +transaction { + prepare(signer: auth(LoadValue) &Account) {} + + execute { + // Invalid: Cannot access the `signer` account reference, as it is not in scope + let resource <- signer.storage.load<@Resource>(from: /storage/resource) + destroy resource + + // Valid: Can obtain an unauthorized reference to any account + let otherAccount = getAccount(0x3) + } +} +``` + +### Post-conditions + +Transaction post-conditions are just like +[post-conditions of functions](./functions.mdx#function-preconditions-and-postconditions). + +Post-conditions are optional and are declared in a `post` block. +They are executed after the execution phase, +and are used to verify that the transaction logic has been executed properly. +The block can have zero or more conditions. + +For example, a token transfer transaction can ensure that the final balance has a certain value: + +```cadence +post { + signer.balance == 30.0: "Balance after transaction is incorrect!" +} +``` + +If any of the post-conditions fail, +then the transaction fails and is completely reverted. + +### Pre-conditions and post-conditions + +Another function of the pre-conditions and post-conditions +is to describe the effects of a transaction on the involved accounts. +They are essential for users to verify what a transaction does before submitting it. +The conditions an easy way to introspect transactions before they are executed. + +For example, the software that a user uses to sign and send a transaction +could analyze and interpret the transaction into a human-readable description, like +"This transaction will transfer 30 tokens from A to B. +The balance of A will decrease by 30 tokens and the balance of B will increase by 30 tokens." + +## Summary + +Transactions use phases to make the transaction's code / intent more readable. +They provide a way for developers to separate the transaction logic. +Transactions also provide a way to check the state prior / after transaction execution, +to prevent the transaction from running, or reverting changes made by the transaction if needed. + +The following is a brief summary of how to use the `prepare`, `pre`, `execute`, +and `post` blocks in a transaction to implement the transaction's phases: + +```cadence +transaction { + prepare(signer1: &Account) { + // Access signing accounts of the transaction. + // + // Avoid logic that does not need access to the signing accounts. + // + // The signing accounts can't be accessed anywhere else in the transaction. + } + + pre { + // Define conditions that must be true + // for the transaction to execute. + // + // Define the expected state of things + // as they should be before the transaction is executed. + } + + execute { + // The main transaction logic goes here, but you can access + // any public information or resources published by any account. + } + + post { + // Define conditions that must be true + // for the transaction to be committed. + // + // Define the expected state of things + // as they should be after the transaction executed. + // + // Also used to provide information about what changes + // the transaction will make to the signing accounts. + } +} +``` diff --git a/versioned_docs/version-current_0.42/0.42/language/type-annotations.md b/versioned_docs/version-1.0/language/type-annotations.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/type-annotations.md rename to versioned_docs/version-1.0/language/type-annotations.md diff --git a/versioned_docs/version-current_0.42/0.42/language/type-hierarchy.md b/versioned_docs/version-1.0/language/type-hierarchy.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/type-hierarchy.md rename to versioned_docs/version-1.0/language/type-hierarchy.md diff --git a/versioned_docs/version-current_0.42/0.42/language/type-hierarchy.monopic b/versioned_docs/version-1.0/language/type-hierarchy.monopic similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/type-hierarchy.monopic rename to versioned_docs/version-1.0/language/type-hierarchy.monopic diff --git a/versioned_docs/version-current_0.42/0.42/language/type-hierarchy.png b/versioned_docs/version-1.0/language/type-hierarchy.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/type-hierarchy.png rename to versioned_docs/version-1.0/language/type-hierarchy.png diff --git a/versioned_docs/version-current_0.42/0.42/language/type-inference.md b/versioned_docs/version-1.0/language/type-inference.md similarity index 98% rename from versioned_docs/version-current_0.42/0.42/language/type-inference.md rename to versioned_docs/version-1.0/language/type-inference.md index a076163..8f02e9d 100644 --- a/versioned_docs/version-current_0.42/0.42/language/type-inference.md +++ b/versioned_docs/version-1.0/language/type-inference.md @@ -109,7 +109,7 @@ let add = (a: Int8, b: Int8): Int { return a + b } -// `add` has type `((Int8, Int8): Int)` +// `add` has type `fun(Int8, Int8): Int` ``` Type inference is performed for each expression / statement, and not across statements. diff --git a/versioned_docs/version-current_0.42/0.42/language/type-safety.md b/versioned_docs/version-1.0/language/type-safety.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/language/type-safety.md rename to versioned_docs/version-1.0/language/type-safety.md diff --git a/versioned_docs/version-current_0.42/0.42/language/values-and-types.mdx b/versioned_docs/version-1.0/language/values-and-types.mdx similarity index 89% rename from versioned_docs/version-current_0.42/0.42/language/values-and-types.mdx rename to versioned_docs/version-1.0/language/values-and-types.mdx index 39b4763..df74f86 100644 --- a/versioned_docs/version-current_0.42/0.42/language/values-and-types.mdx +++ b/versioned_docs/version-1.0/language/values-and-types.mdx @@ -99,6 +99,8 @@ and can represent values in the following ranges: - **`Word16`**: 0 through 2^16 − 1 (65535) - **`Word32`**: 0 through 2^32 − 1 (4294967295) - **`Word64`**: 0 through 2^64 − 1 (18446744073709551615) +- **`Word128`**: 0 through 2^128 − 1 (340282366920938463463374607431768211455) +- **`Word256`**: 0 through 2^256 − 1 (115792089237316195423570985008687907853269984665640564039457584007913129639935) The types are independent types, i.e. not subtypes of each other. @@ -948,6 +950,21 @@ let c = str[0] // is the Character "a" let uwu: String = String.fromCharacters(rawUwU) // "UwU" ``` +- + ```cadence + let utf8: [UInt8] + ``` + + The byte array of the UTF-8 encoding + + ```cadence + let a: Character = "a" + let a_bytes = a.utf8 // `a_bytes` is `[97]` + + let bouquet: Character = "\u{1F490}" + let bouquet_bytes = bouquet.utf8 // `bouquet_bytes` is `[240, 159, 146, 144]` + ``` + ## Arrays Arrays are mutable, ordered collections of values. @@ -1099,6 +1116,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence + access(all) fun concat(_ array: T): T ``` @@ -1128,6 +1146,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence + access(all) fun contains(_ element: T): Bool ``` @@ -1153,6 +1172,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence + access(all) fun firstIndex(of: T): Int? ``` @@ -1174,6 +1194,7 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. - ```cadence + access(all) fun slice(from: Int, upTo: Int): [T] ``` @@ -1199,6 +1220,164 @@ are available for both variable-sized and fixed-sized or variable-sized arrays. let invalidIndices = example.slice(from: 2, upTo: 1) ``` +- + ```cadence + access(all) + fun reverse(): [T] + ``` + + Returns a new array with contents in the reversed order. + Available if `T` is not resource-kinded. + + ```cadence + let example = [1, 2, 3, 4] + + // Create a new array which is the reverse of the original array. + let reversedExample = example.reverse() + // `reversedExample` is now `[4, 3, 2, 1]` + ``` + + ```cadence + access(all) + fun reverse(): [T; N] + ``` + + Returns a new fixed-sized array of same size with contents in the reversed order. + + ```cadence + let fixedSizedExample: [String; 3] = ["ABC", "XYZ", "PQR"] + + // Create a new array which is the reverse of the original array. + let fixedArrayReversedExample = fixedSizedExample.reverse() + // `fixedArrayReversedExample` is now `["PQR", "XYZ", "ABC"]` + ``` + +- + ```cadence + access(all) + fun map(_ f: fun(T): U): [U] + ``` + + Returns a new array whose elements are produced by applying the mapper function on each element + of the original array. + Available if `T` is not resource-kinded. + + ```cadence + let example = [1, 2, 3] + let trueForEven = + fun (_ x: Int): Bool { + return x % 2 == 0 + } + + let mappedExample: [Bool] = example.map(trueForEven) + // `mappedExample` is `[False, True, False]` + // `example` still remains as `[1, 2, 3]` + + // Invalid: Map using a function which accepts a different type. + // This results in a type error, as the array contains `Int` values while function accepts + // `Int64`. + let functionAcceptingInt64 = + fun (_ x: Int64): Bool { + return x % 2 == 0 + } + let invalidMapFunctionExample = example.map(functionAcceptingInt64) + ``` + + `map` function is also available for fixed-sized arrays: + + ```cadence + access(all) + fun map(_ f: fun(T): U): [U; N] + ``` + + Returns a new fixed-sized array whose elements are produced by applying the mapper function on + each element of the original array. + Available if `T` is not resource-kinded. + + ```cadence + let fixedSizedExample: [String; 3] = ["ABC", "XYZYX", "PQR"] + let lengthOfString = + fun (_ x: String): Int { + return x.length + } + + let fixedArrayMappedExample = fixedSizedExample.map(lengthOfString) + // `fixedArrayMappedExample` is now `[3, 5, 3]` + // `fixedSizedExample` still remains as ["ABC", "XYZYX", "PQR"] + + // Invalid: Map using a function which accepts a different type. + // This results in a type error, as the array contains `String` values while function accepts + // `Bool`. + let functionAcceptingBool = + fun (_ x: Bool): Int { + return 0 + } + let invalidMapFunctionExample = fixedSizedExample.map(functionAcceptingBool) + ``` + +- + ```cadence + access(all) + fun filter(_ f: fun(T): Bool): [T] + ``` + + Returns a new array whose elements are filtered by applying the filter function on each element + of the original array. + Available if `T` is not resource-kinded. + + ```cadence + let example = [1, 2, 3] + let trueForEven = + fun (_ x: Int): Bool { + return x % 2 == 0 + } + + let filteredExample: [Int] = example.filter(trueForEven) + // `filteredExample` is `[2]` + // `example` still remains as `[1, 2, 3]` + + // Invalid: Filter using a function which accepts a different type. + // This results in a type error, as the array contains `Int` values while function accepts + // `Int64`. + let functionAcceptingInt64 = + fun (_ x: Int64): Bool { + return x % 2 == 0 + } + let invalidFilterFunctionExample = example.filter(functionAcceptingInt64) + ``` + + `filter` function is also available for fixed-sized arrays: + + ```cadence + access(all) + fun filter(_ f: fun(T): Bool): [T] + ``` + + Returns a new **variable-sized** array whose elements are filtered by applying the filter function on each element + of the original array. + Available if `T` is not resource-kinded. + + ```cadence + let fixedSizedExample: [String; 3] = ["AB", "XYZYX", "PQR"] + let lengthOfStringGreaterThanTwo = + fun (_ x: String): Bool { + return x.length > 2 + } + + let fixedArrayFilteredExample = fixedSizedExample.filter(lengthOfStringGreaterThanTwo) + // `fixedArrayFilteredExample` is `["XYZYX", "PQR"]` + // `fixedSizedExample` still remains as ["AB", "XYZYX", "PQR"] + + // Invalid: Filter using a function which accepts a different type. + // This results in a type error, as the array contains `String` values while function accepts + // `Bool`. + let functionAcceptingBool = + fun (_ x: Bool): Bool { + return True + } + let invalidFilterFunctionExample = fixedSizedExample.filter(functionAcceptingBool) + ``` + #### Variable-size Array Functions The following functions can only be used on variable-sized arrays. @@ -1206,6 +1385,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence + access(Mutate | Insert) fun append(_ element: T): Void ``` @@ -1229,6 +1409,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence + access(Mutate | Insert) fun appendAll(_ array: T): Void ``` @@ -1253,6 +1434,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence + access(Mutate | Insert) fun insert(at: Int, _ element: T): Void ``` @@ -1284,6 +1466,7 @@ It is invalid to use one of these functions on a fixed-sized array. ``` - ```cadence + access(Mutate | Remove) fun remove(at: Int): T ``` @@ -1309,6 +1492,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence + access(Mutate | Remove) fun removeFirst(): T ``` @@ -1339,6 +1523,7 @@ It is invalid to use one of these functions on a fixed-sized array. - ```cadence + access(Mutate | Remove) fun removeLast(): T ``` @@ -1521,6 +1706,7 @@ booleans[0] = true - ```cadence + access(Mutate | Insert) fun insert(key: K, _ value: V): V? ``` @@ -1587,6 +1773,7 @@ booleans[0] = true - ```cadence + access(Mutate | Remove) let keys: [K] ``` @@ -1627,6 +1814,7 @@ booleans[0] = true - ```cadence + access(all) fun containsKey(key: K): Bool ``` @@ -1652,7 +1840,8 @@ booleans[0] = true - ```cadence - fun forEachKey(_ function: ((K): Bool)): Void + access(all) + fun forEachKey(_ function: fun(K): Bool): Void ``` Iterate through all the keys in the dictionary, exiting early if the passed function returns false. diff --git a/versioned_docs/version-current_0.42/0.42/measuring-time.mdx b/versioned_docs/version-1.0/measuring-time.mdx similarity index 98% rename from versioned_docs/version-current_0.42/0.42/measuring-time.mdx rename to versioned_docs/version-1.0/measuring-time.mdx index 0b23db7..4355086 100644 --- a/versioned_docs/version-current_0.42/0.42/measuring-time.mdx +++ b/versioned_docs/version-1.0/measuring-time.mdx @@ -26,7 +26,7 @@ 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](https://developers.flow.com/references/run-and-secure/nodes/faq/operators.mdx#does-the-blockheight-go-up-1-every-second). +as [the block rate naturally fluctuates](https://developers.flow.com/build/run-and-secure/nodes/faq/operators.mdx#does-the-blockheight-go-up-1-every-second). In addition to the natural variation described above, there are several theoretical block production attacks that could skew this relationship even further. diff --git a/versioned_docs/version-current_0.42/0.42/project-development-tips.md b/versioned_docs/version-1.0/project-development-tips.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/project-development-tips.md rename to versioned_docs/version-1.0/project-development-tips.md diff --git a/versioned_docs/version-current_0.42/0.42/security-best-practices.md b/versioned_docs/version-1.0/security-best-practices.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/security-best-practices.md rename to versioned_docs/version-1.0/security-best-practices.md diff --git a/versioned_docs/version-current_0.42/0.42/solidity-to-cadence.md b/versioned_docs/version-1.0/solidity-to-cadence.md similarity index 93% rename from versioned_docs/version-current_0.42/0.42/solidity-to-cadence.md rename to versioned_docs/version-1.0/solidity-to-cadence.md index b047568..4b69fa1 100644 --- a/versioned_docs/version-current_0.42/0.42/solidity-to-cadence.md +++ b/versioned_docs/version-1.0/solidity-to-cadence.md @@ -1,5 +1,6 @@ --- -title: Guide for Solidity developers +title: Guide for Solidity Developers +sidebar_label: Guide for Solidity Developers sidebar_position: 8 --- Cadence introduces a different way to approach smart contract development which may feel unfamiliar to @@ -12,7 +13,7 @@ up while transitioning. # Conceptual foundations for Cadence -A fundamental difference to get used to when adjusting to Cadence from Solidity is of mindset. Security and +A fundamental difference to get used to when adjusting to Cadence from Solidity is mindset. Security and interoperability on Ethereum are designed around addresses (or more specifically the account associated with an address), resulting in all contracts having to carefully track and evaluate access and authorizations. @@ -50,14 +51,17 @@ There is only one account type in Cadence also with an account address, similar Accounts realize ownership on Flow in being the container where keys, Resources, and contracts are stored on-chain. -### PublicAccount and AuthAccount +## Account -`PublicAccount` is the publicly available part of an account through which public functions or objects can be -accessed by any account using the `getAccount` function. +`Account` is the type that provides access to an account. -`AuthAccount` is available only to the signer of the transaction. An `AuthAccount` reference provides full access -to that account's storage, keys configuration, and contract code. Capabilities ensure that resources held in an account can be -safely shared/accessed; access to `AuthAccount` should never be given to other accounts. +The `getAccount` function allows getting access to the publicly available functions and fields of an account. +For example, this allows querying an accounts balance. + +An authorized `Account` reference provides access and allows the management of, +for instance, the account's storage, keys configuration, and contract code. +An authorized `Account` reference can only be acquired by signing a transaction. +Capabilities ensure that resources held in an account can be safely shared/accessed. ## Resources @@ -75,7 +79,7 @@ without any explicit handling needed. ## Capability-based access -Remote access to stored objects can be managed via [Capabilities](./language#capability-based-access-control). This +Remote access to stored objects can be managed via [Capabilities](./language/capabilities.md). This means that if an account wants to be able to access another account's stored objects, it must have been provided with a valid Capability to that object. Capabilities can be either public or private. An account can share a public Capability if it wants to give all other accounts access. (For example, it’s common for an account to accept fungible @@ -86,7 +90,7 @@ controls minting through an “administrator Capability” that grants specific ## Contract standards There are numerous widely-used contract standards established to benefit the ecosystem, for example -[Fungible Token](https://developers.flow.com/build/flow.md#flow-token)(FT) and [Non-Fungible Token](https://developers.flow.com/build/flow.md#flow-nft#overview)(NFT) +[Fungible Token](https://developers.flow.com/build/flow.md#flow-token)(FT) and [Non-Fungible Token](https://developers.flow.com/build/flow#overview)(NFT) standards which are conceptually equivalent to Ethereum's ERC-20 and ERC-721 standards. Cadence's object-oriented design means standards apply through contract sub-types such as Resources, Resource interfaces, or other types declared in the contract standard. Standards can define and limit behaviour and/or set conditions which @@ -150,13 +154,12 @@ the opposite direction than the [access-based security](https://en.wikipedia.org ## Access control using Capabilities -Solidity lacks specific types or other primitives to aid with permission management. Developers have to inline -guards to `require` at every function entry-point, thus validating the `msg.sender` of the transaction. +Solidity lacks specific types or other primitives to aid with permission management. +Developers have to inline guards to `require` at every function entry-point, +thus validating the `msg.sender` of the transaction. [Capabilities](./language/capabilities.md) are defined by linking storage paths (namespaces for contract -storage) to protected objects and then making that linked capability available to other accounts. Public and private -scopes defined for storage paths and Capabilities themselves align precisely with -[PublicAccount](./language/accounts.mdx#publicaccount) / [AuthAccount](./language/accounts.mdx#authaccount) account scopes. +storage) to protected objects and then making that linked capability available to other accounts. Any account can get access to an account's public Capabilities. Public capabilities are created using public paths, i.e. they have the domain `public`. For example, all accounts have a default public capability linked to the @@ -164,13 +167,12 @@ i.e. they have the domain `public`. For example, all accounts have a default pub interface, to allow any account to `borrow()` a reference to the Vault to make a `deposit()`. Since only the functions defined under the `[FungibleToken.Receiver](https://github.com/onflow/flow-ft/blob/master/contracts/FungibleToken.cdc#L105)` interface are exposed, the borrower of the vault reference cannot call `withdraw()` since it is scoped in the `provider` -interface. Public capabilities can be obtained from both authorized accounts (`AuthAccount`) and public accounts -(`PublicAccount`). +interface. Private capabilities are specifically granted to accounts. They are created using private paths, i.e. they have the domain `private`. After creation, they can be obtained from authorised account objects (`AuthAccount`) but not public accounts (`PublicAccount`). To share a private Capability with another account, the owning account must `publish` it -to another account which places in the [account inbox](./language/accounts.mdx#account-inbox). The recipient can later +to another account which places in the [account inbox](./language/accounts/inbox.mdx). The recipient can later claim the Capability from the account inbox using then `claim` function. Capabilities can be `unpublished` and can also be [revoked](./design-patterns.md#capability-revocation) by the creating @@ -258,8 +260,7 @@ Cadence provides an `assert` operator which mirrors `assert` in Solidity. Modifiers are extensively used in Solidity when enforcing pre-checks within a function. This is a powerful language feature. However, modifiers can also mutate state which introduces risks to program control flow. -Cadence uses `pre` and `post` blocks to validate input values or the function execution outputs. Notably, `pre` and -`post` block prohibit changing of state and may only enforce conditions. +Cadence uses `pre` and `post` blocks to validate input values or the function execution outputs. Notably, `pre` and `post` block prohibit changing of state and may only enforce conditions. Another difference is that modifiers in Solidity can be re-used within the contract multiple times. Cadence `pre` & `post` blocks are associated to individual functions only, reducing the likelihood of errors but which @@ -289,10 +290,10 @@ Scripts are read-only in nature, requiring only a `main` function declaration an import FungibleToken from "../../contracts/FungibleToken.cdc" import ExampleToken from "../../contracts/ExampleToken.cdc" -pub fun main(account: Address): UFix64 { +access(all) fun main(account: Address): UFix64 { let acct = getAccount(account) - let vaultRef = acct.getCapability(ExampleToken.VaultPublicPath) - .borrow<&ExampleToken.Vault{FungibleToken.Balance}>() + let vaultRef = acct.capabilities + .borrow<&ExampleToken.Vault>(ExampleToken.VaultPublicPath) ?? panic("Could not borrow Balance reference to the Vault") return vaultRef.balance @@ -318,7 +319,7 @@ transaction(addressAmountMap: {Address: UFix64}) { prepare(signer: AuthAccount) { // Get a reference to the signer's stored vault - self.vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath) + self.vaultRef = signer.storage.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath) ?? panic("Could not borrow reference to the owner's Vault!") } @@ -333,8 +334,8 @@ transaction(addressAmountMap: {Address: UFix64}) { let recipient = getAccount(address) // Get a reference to the recipient's Receiver - let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath) - .borrow<&{FungibleToken.Receiver}>() + let receiverRef = recipient.capabilities + .borrow<&{FungibleToken.Receiver}>(ExampleToken.ReceiverPublicPath) ?? panic("Could not borrow receiver reference to the recipient's Vault") // Deposit the withdrawn tokens in the recipient's receiver @@ -374,7 +375,7 @@ Cadence offers a wide range of options to implement various multi-signature sche - Inherent support for multi-sign transactions. - Resource transfer scheme. -- Inherent support of BLS signature scheme. +- Inherent support of the BLS signature scheme. Flow account keys have assigned weights, where a 1000 unit weight is the cumulative weight needed from signing keys to execute a transaction successfully. One can divide weights across multiple keys and distribute those partial @@ -461,7 +462,7 @@ program flow similar to `if nil; else; end;`. ## Iterable Dictionaries Solidity offers the mapping type, however, it is not iterable. Because of that dApp developers have to maintain -off-chain tracking to be have access to keys. This also pushes builders to create custom datatypes like `EnumerableMap` +off-chain tracking to have access to keys. This also pushes builders to create custom datatypes like `EnumerableMap` which adds to gas costs. Cadence offers the [Dictionary](./language/control-flow.md) type, an unordered collection of key-value associations @@ -481,7 +482,7 @@ provides: ## Argument labelling -Argument labels in Cadence help to disambiguate input values. They makes code more readable and explicit. They +Argument labels in Cadence help to disambiguate input values. They make code more readable and explicit. They also eliminate confusion around the order of arguments when working with the same type. They must be included in the function call. This restriction can be skipped if the label is preceded by `_ ` on its declaration. Eg: @@ -493,4 +494,9 @@ called as can be called as `self.foo(balance: 30.0)` or as -`self.foo(30.0)` \ No newline at end of file +`self.foo(30.0)` + +## Additional resources + +* Cadence or Solidity: [On-Chain Token Transfer Deep Dive](https://flow.com/engineering-blogs/flow-blockchain-programming-language-smart-contract-cadence-solidity-comparison-ethereum) +* Implementing the [Bored Ape Yacht Club](https://flow.com/post/implementing-the-bored-ape-yacht-club-smart-contract-in-cadence) smart contract in Cadence \ No newline at end of file diff --git a/versioned_docs/version-current_0.42/0.42/testing-framework.mdx b/versioned_docs/version-1.0/testing-framework.mdx similarity index 98% rename from versioned_docs/version-current_0.42/0.42/testing-framework.mdx rename to versioned_docs/version-1.0/testing-framework.mdx index a5f6226..541ac95 100644 --- a/versioned_docs/version-current_0.42/0.42/testing-framework.mdx +++ b/versioned_docs/version-1.0/testing-framework.mdx @@ -878,7 +878,7 @@ access(all) struct Transaction { } ``` -The number of authorizers must match the number of `AuthAccount` arguments in the `prepare` block of the transaction. +The number of authorizers must match the number of `&Account` parameters in the `prepare` block of the transaction. ```cadence import Test @@ -890,7 +890,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: AuthAccount) {} execute{} }", + code: "transaction { prepare(acct: &Account) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -906,7 +906,7 @@ access(all) fun testExample() { access(all) fun testExampleTwo() { let tx = Test.Transaction( - code: "transaction { prepare(acct: AuthAccount) {} execute{} }", + code: "transaction { prepare(acct: &Account) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -950,7 +950,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: AuthAccount) {} execute{} }", + code: "transaction { prepare(acct: &Account) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -1124,7 +1124,7 @@ access(all) fun testExample() { Test.expect(scriptResult, Test.beSucceeded()) let tx = Test.Transaction( - code: "transaction { prepare(acct: AuthAccount) {} execute{} }", + code: "transaction { prepare(acct: &Account) {} execute{} }", authorizers: [account.address], signers: [account], arguments: [], @@ -1272,7 +1272,7 @@ access(all) let account = blockchain.createAccount() access(all) fun testExample() { let tx = Test.Transaction( - code: "transaction { prepare(acct: AuthAccount) {} execute{ log(\"in a transaction\") } }", + code: "transaction { prepare(acct: &Account) {} execute{ log(\"in a transaction\") } }", authorizers: [account.address], signers: [account], arguments: [], diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/01-first-steps.md b/versioned_docs/version-1.0/tutorial/01-first-steps.md similarity index 97% rename from versioned_docs/version-current_0.42/0.42/tutorial/01-first-steps.md rename to versioned_docs/version-1.0/tutorial/01-first-steps.md index 8ba3a70..0ae326a 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/01-first-steps.md +++ b/versioned_docs/version-1.0/tutorial/01-first-steps.md @@ -14,7 +14,7 @@ Cadence introduces new features to smart contract programming that help develope - Type safety and a strong static type system - Resource-oriented programming, a new paradigm that pairs linear types with object capabilities to create a secure and declarative model for digital ownership by ensuring that resources (and their associated assets) can only exist in one location at a time, cannot be copied, and cannot be accidentally lost or deleted -- Built-in pre-conditions and post-conditions for functions and [transactions](../language/transactions) +- Built-in pre-conditions and post-conditions for functions and [transactions](../language/transactions.md) - The utilization of capability-based security, which enforces access control by requiring that access to objects is restricted to only the owner and those who have a valid reference to the object @@ -62,7 +62,7 @@ to deploy that contract to the currently selected account. After a few seconds, the contract should deploy. In the accounts section, you should now see the name of the contract next to the selected account that you deployed too -and if you click on "Log" in the bottom section of the scrren, you should +and if you click on "Log" in the bottom section of the screen, you should see a message in the console confirming that the contract was deployed and which account it was deployed to. You can also select transactions and scripts from the left selection menu @@ -81,4 +81,4 @@ or you can use the pre-generated tutorial setups in the Playground. ## Say Hello, World! Now that you have the Flow Developer Playground running, -you can [create a smart contract](./02-hello-world.md) for Flow! \ No newline at end of file +you can [create a smart contract](./02-hello-world.md) for Flow! diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/02-hello-world.md b/versioned_docs/version-1.0/tutorial/02-hello-world.md similarity index 94% rename from versioned_docs/version-current_0.42/0.42/tutorial/02-hello-world.md rename to versioned_docs/version-1.0/tutorial/02-hello-world.md index 0361e80..de69a8c 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/02-hello-world.md +++ b/versioned_docs/version-1.0/tutorial/02-hello-world.md @@ -90,7 +90,7 @@ It also allows you to save and share your work with others so that you can test When you work with accounts in the Flow Playground, you start with five default accounts that you can change and reconfigure. Each account in your environment has a unique address, and you can select an account in the left toolbar, -which will open up the contracts that are saved for that account. +which will open up the contracts that are saved for that account. The `HelloWorld` contracts are loaded by default for each account unless you load an existing playground project with other saved contracts. @@ -143,44 +143,44 @@ Open the Account `0x01` tab with the file called ```cadence HelloWorld.cdc // HelloWorld.cdc // -pub contract HelloWorld { +access(all) contract HelloWorld { // Declare a public field of type String. // - // All fields must be initialized in the init() function. - pub let greeting: String + // All fields must be initialized in the initializer. + access(all) let greeting: String - // The init() function is required if the contract contains any fields. + // The initializer is required if the contract contains any fields. init() { self.greeting = "Hello, World!" } // Public function that returns our friendly greeting! - pub fun hello(): String { + access(all) fun hello(): String { return self.greeting } } ``` -The line `pub contract HelloWorld ` declares a contract that is accessible in all scopes (public). -It's followed by `pub let greeting: String` which declares a state constant (`let`) of type `String` that is accessible in all scopes(`pub`). +The line `access(all) contract HelloWorld ` declares a contract that is accessible in all scopes (public). +It's followed by `access(all) let greeting: String` which declares a state constant (`let`) of type `String` that is accessible in all scopes(`access(all)`). You would have used `var` to declare a variable, which means that the value can be changed later on instead of remaining constant like with `let`. -You can use `access(all)` and the `pub` keyword interchangeably. +You can use `access(all)` and the `access(all)` keyword interchangeably. They are both examples of an access control specification that means an interface can be accessed in all scopes, but not written to in all scopes. For more information about the different levels of access control permitted in Cadence, refer to the [Access Control section of the language reference](../language/access-control). The `init()` section is called the initializer. It is a special function that only runs when the contract is first created. Objects similar to contracts, such as other [composite types like structs or resources](../language/composite-types), -require that the `init()` function initialize any fields that are declared in a composite type. +require that the initializer initializes all fields that are declared in a composite type. In the above example, the initializer sets the `greeting` field to `"Hello, World!"` when the contract is initialized. The last part of our `HelloWorld` contract is a public function called `hello()`. This declaration returns a value of type `String`. Anyone who imports this contract in their transaction or script can read the public fields, -use the public types, and call the public contract functions; i.e. the ones that have `pub` or `access(all)` specified. +use the public types, and call the public contract functions; i.e. the ones that have `access(all)` or `access(all)` specified. Soon you'll deploy this contract to your account and run a transaction that calls its function, but first, let's look at what accounts and transactions are. @@ -266,7 +266,7 @@ import HelloWorld from 0x01 transaction { - prepare(acct: AuthAccount) {} + prepare(acct: &Account) {} execute { log(HelloWorld.hello()) diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/03-resources.md b/versioned_docs/version-1.0/tutorial/03-resources.md similarity index 89% rename from versioned_docs/version-current_0.42/0.42/tutorial/03-resources.md rename to versioned_docs/version-1.0/tutorial/03-resources.md index 42545e3..3028c2f 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/03-resources.md +++ b/versioned_docs/version-1.0/tutorial/03-resources.md @@ -53,8 +53,8 @@ Resources are one of Cadence's defining features. In Cadence, resources are a composite type like a struct or a class, but with some special rules. Here is an example definition of a resource: ```cadence -pub resource Money { - pub let balance: Int +access(all) resource Money { + access(all) let balance: Int init() { self.balance = 0 @@ -84,7 +84,7 @@ To interact with resources, you'll learn a few important concepts: - Using the create keyword - The move operator `<-` -- The [Account Storage API](../language/accounts#account-storage-api) +- The [Account Storage API](../language/accounts/storage.mdx) Let's start by looking at how to create a resource with the `create` keyword and the move operator `<-`. @@ -103,21 +103,21 @@ Open the Account `0x01` tab with file named `HelloWorldResource.cdc`.
```cadence HelloWorldResource.cdc -pub contract HelloWorld { +access(all) contract HelloWorld { // Declare a resource that only includes one function. - pub resource HelloAsset { + access(all) resource HelloAsset { // A transaction can call this function to get the "Hello, World!" // message from the resource. - pub fun hello(): String { + access(all) fun hello(): String { return "Hello, World!" } } // We're going to use the built-in create function to create a new instance // of the HelloAsset resource - pub fun createHelloAsset(): @HelloAsset { + access(all) fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } @@ -135,8 +135,8 @@ Deploy this code to account `0x01` using the `Deploy` button. We start by declaring a new `HelloWorld` contract in account `0x01`, inside this new `HelloWorld` contract we: -1. Declare the resource `HelloAsset` with public scope `pub` -2. Declare the resource function `hello()` inside `HelloAsset` with public scope `pub` +1. Declare the resource `HelloAsset` with public scope `access(all)` +2. Declare the resource function `hello()` inside `HelloAsset` with public scope `access(all)` 3. Declare the contract function `createHelloAsset()` which `create`s a `HelloAsset` resource 4. The `createHelloAsset()` function uses the move operator (`<-`) to return the resource @@ -154,8 +154,8 @@ Let's walk through this contract in more detail, starting with the resource. Resources are one of the most important things that Cadence introduces to the smart contract design experience: ```cadence -pub resource HelloAsset { - pub fun hello(): String { +access(all) resource HelloAsset { + access(all) fun hello(): String { return "Hello, World!" } } @@ -179,18 +179,18 @@ init() { ``` All composite types like contracts, resources, -and structs can have an optional `init()` function that only runs when the object is initially created. +and structs can have an optional initializer that only runs when the object is initially created. Cadence requires that all fields must be explicitly initialized, so if the object has any fields, this function has to be used to initialize them. -Contracts also have read and write access to the storage of the account that they are deployed to by using the built-in -[`self.account`](../language/contracts) object. -This is an [`AuthAccount` object](../language/accounts#authaccount) -that gives them access to many different functions to interact with the private storage of the account. - -This contract's `init` function is simple, it logs the phrase `"Hello Asset"` to the console. +Contracts also have read and write access to the storage of the account that they are deployed to +by using the built-in [`self.account`](../language/contracts.mdx) field. +This is an [account reference](../language/accounts/index.mdx) (`&Account`), +authorized to access and manage all aspects of the account, +such as account storage, keys, and contracts. +This contract's initializer is simple, it logs the phrase `"Hello Asset"` to the console. **A resource can only be created in the scope that it is defined in.** @@ -200,7 +200,7 @@ This prevents anyone from being able to create arbitrary amounts of resource obj In this example, we declared a function that can create `HelloAsset` resources: ```cadence -pub fun createHelloAsset(): @HelloAsset { +access(all) fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } ``` @@ -260,12 +260,12 @@ import HelloWorld from 0x01 transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(SaveValue) &Account) { // Here we create a resource and move it to the variable newHello, // then we save it in the account storage let newHello <- HelloWorld.createHelloAsset() - acct.save(<-newHello, to: /storage/HelloAssetTutorial) + acct.storage.save(<-newHello, to: /storage/HelloAssetTutorial) } // In execute, we log a string to confirm that the transaction executed successfully. @@ -283,13 +283,11 @@ Here's what this transaction does: 4. `log` the text `HelloAsset created and stored` to the console. This is our first transaction using the `prepare` phase! -The `prepare` phase is the only place that has access to the signing accounts' -[private `AuthAccount` object](../language/accounts#authaccount). -`AuthAccount` objects have many different methods that are used to interact with account storage. -You can see the documentation for all of these in the [account section of the language reference](../language/accounts#authaccount). -In this tutorial, we'll be using `AuthAccount` methods to save and load from `/storage/`. -The `prepare` phase can also create `/private/` and `/public/` links to the objects in `/storage/`, -called [capabilities](../language/capabilities) (more on these later). +The `prepare` phase is the only place that has access to the signing accounts, +via [account references (`&Account`)](../language/accounts/index.mdx). +Account references have access to many different methods that are used to interact with account, e.g., the account's storage. +You can see the documentation for all of these in the [account section of the language reference](../language/accounts/index.mdx). +In this tutorial, we'll be using account functions to save to and load from account storage (`/storage/`). By not allowing the execute phase to access account storage, we can statically verify which assets and areas of the signers' storage a given transaction can modify. @@ -306,13 +304,13 @@ let newHello <- HelloWorld.createHelloAsset() ``` Next, we save the resource to the account storage. -We use the [account storage API](../language/accounts#account-storage-api) to interact with the account storage in Flow. +We use the [account storage API](../language/accounts/storage.mdx) to interact with the account storage in Flow. To save the resource, we'll be using the -[`save()`](../language/accounts#account-storage-api) +[`save()`](../language/accounts/storage.mdx) method from the account storage API to store the resource in the account at the path `/storage/HelloAssetTutorial`. ```cadence -acct.save(<-newHello, to: /storage/HelloAssetTutorial) +acct.storage.save(<-newHello, to: /storage/HelloAssetTutorial) ``` The first parameter to `save` is the object that is being stored, and the `to` parameter is the path that the object is being stored at. @@ -373,14 +371,14 @@ This should open a view of the different contracts and objects in the account. You should see this entry for the `HelloWorld` contract and the `HelloAsset` resource: ``` -Deployed Contracts: +Deployed Contracts: [ { "contract": "HelloWorld", "height": 6 } -] -Account Storage: +] +Account Storage: { "Private": null, "Public": {}, @@ -433,11 +431,11 @@ import HelloWorld from 0x01 transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(LoadValue, SaveValue) &Account) { // Load the resource from storage, specifying the type to load it as // and the path where it is stored - let helloResource <- acct.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) + let helloResource <- acct.storage.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) // We use optional chaining (?) because the value in storage // may or may not exist, and thus is considered optional. @@ -446,7 +444,7 @@ transaction { // Put the resource back in storage at the same spot // We use the force-unwrap operator `!` to get the value // out of the optional. It aborts if the optional is nil - acct.save(<-helloResource!, to: /storage/HelloAssetTutorial) + acct.storage.save(<-helloResource!, to: /storage/HelloAssetTutorial) } } ``` @@ -455,18 +453,18 @@ Here's what this transaction does: 1. Import the `HelloWorld` definitions from account `0x01` 2. Moves the `HelloAsset` object from storage to `helloResource` with the move operator - and the `load` function from the [account storage API](../language/accounts#account-storage-api) + and the `load` function from the [account storage API](../language/accounts/storage.mdx) 3. Calls the `hello()` function of the `HelloAsset` resource stored in `helloResource` and logs the result 4. Saves the resource in the account that we originally moved it from at the path `/storage/HelloAssetTutorial` -We're going to be using the `prepare` phase again to load the resource because it -has access to the signing accounts' [private `AuthAccount` object](../language/accounts#authaccount). +We're going to be using the `prepare` phase again to load the resource +using the [reference to the account](../language/accounts/index.mdx) that is passed in. Let's go over the transaction in more detail. -To remove an object from storage, we use the `load` method from the [account storage API](../language/accounts#account-storage-api) +To remove an object from storage, we use the `load` method from the [account storage API](../language/accounts/storage.mdx) ```cadence -let helloResource <- acct.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) +let helloResource <- acct.storage.load<@HelloWorld.HelloAsset>(from: /storage/HelloAssetTutorial) ``` If no object of the specified type is stored under the given path, the function returns nothing, or `nil`. @@ -515,7 +513,7 @@ However, if the stored value was `nil`, the function call would not occur and th Next, we use `save` again to put the object back in storage in the same spot: ```cadence -acct.save(<-helloResource!, to: /storage/HelloAssetTutorial) +acct.storage.save(<-helloResource!, to: /storage/HelloAssetTutorial) ``` Remember, `helloResource` is still an optional, so we have to handle the possibility that it is `nil`. @@ -562,12 +560,12 @@ Now that you have completed the tutorial, you have the basic knowledge to write - Use the `prepare` phase of a transaction to load resources from account storage Feel free to modify the smart contract to create different resources, -experiment with the available [account storage API](../language/accounts#account-storage-api), +experiment with the available [account storage API](../language/accounts/storage.mdx), and write new transactions and scripts that execute different functions from your smart contract. Have a look at the [resource reference page](../language/resources) to find out more about what you can do with resources. You're on the right track to building more complex applications with Cadence, -now is a great time to check out the [Cadence Best Practices document](../design-patterns) -and [Anti-patterns document](../anti-patterns) +now is a great time to check out the [Cadence Best Practices document](../design-patterns.md) +and [Anti-patterns document](../anti-patterns.md) as your applications become more complex. diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/04-capabilities.md b/versioned_docs/version-1.0/tutorial/04-capabilities.md similarity index 88% rename from versioned_docs/version-current_0.42/0.42/tutorial/04-capabilities.md rename to versioned_docs/version-1.0/tutorial/04-capabilities.md index 69ee465..ffcebbb 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/04-capabilities.md +++ b/versioned_docs/version-1.0/tutorial/04-capabilities.md @@ -40,7 +40,7 @@ socialImageDescription: Capability smart contract image. This tutorial builds on the [previous `Resource` tutorial](./03-resources.md). Before beginning this tutorial, you should have an idea of how accounts,transactions,resources, and signers work with basic field types. This tutorial will build on your understanding of accounts and resources. -You'll learn how to interact with resources using [capabilities](../language/capabilities) +You'll learn how to interact with resources using [capabilities](../language/capabilities.md) In Cadence, resources are a composite type like a struct or a class, but with some special rules: - Each instance of a resource can only exist in exactly one location and cannot be copied. - Resources must be explicitly moved from one location to another when accessed. @@ -65,7 +65,7 @@ in this case, they could create a capability that gives the friend access to onl instead of having to give full control over. Or if a user authenticates a trading app for the first time, -the can could ask the user for a capability object that allows +they could ask the user for a capability object that allows the app to access the trading functionality of a user's account so that the app doesn't need to ask the user for a signature every time. @@ -88,21 +88,21 @@ Open the Account `0x01` tab with file named `HelloWorldResource.cdc`.
```cadence HelloWorldResource-2.cdc -pub contract HelloWorld { +access(all) contract HelloWorld { // Declare a resource that only includes one function. - pub resource HelloAsset { + access(all) resource HelloAsset { // A transaction can call this function to get the "Hello, World!" // message from the resource. - pub fun hello(): String { + access(all) fun hello(): String { return "Hello, World!" } } // We're going to use the built-in create function to create a new instance // of the HelloAsset resource - pub fun createHelloAsset(): @HelloAsset { + access(all) fun createHelloAsset(): @HelloAsset { return <-create HelloAsset() } @@ -210,12 +210,12 @@ You might be confused that we were able to call a method on the `HelloAsset` obj without actually being directly in control of it! It is also stored in the `/storage/` domain of the account, which should be private. -This is because we created a [**capability**](../language/capabilities) for the `HelloAsset` object. +This is because we created a [**capability**](../language/capabilities.md) for the `HelloAsset` object. Capabilities are kind of like pointers in other languages, but which much more fine-grained control. ### Capability Based Access Control -[Capabilities](../language/capabilities) allow the owners of objects +[Capabilities](../language/capabilities.md) allow the owners of objects to specify what functionality of their private objects is available to others. Think of it kind of like an account's API, if you're familiar with the concept. The account owner has private objects stored in their storage, like their collectibles or their money, @@ -253,7 +253,7 @@ The `HelloAsset` object is stored in `/storage/HelloAssetTutorial`, which only t They want any user in the network to be able to call the `hello()` method. So they make a public capability in `/public/HelloAssetTutorial`. To create a capability, we use the `AuthAccount.link` method to link a new capability to an object in storage. -The type contained in `<>` is the restricted reference type that the capability represents. +The type contained in `<>` is the reference type that the capability represents. The capability says that whoever borrows a reference from this capability can only have access to the fields and methods that are specified by the type in `<>`. The specified type has to be a subtype of the type of the object being linked to, @@ -293,7 +293,7 @@ Only one reference to an object can exist at a time, so this type of vulnerabili Additionally, the owner of an object can effectively revoke capabilities they have created by moving the underlying object or destroying the link with the `unlink` method. If the referenced object is moved or the link is destroyed, capabilities that have been created from that link are invalidated. -You can find more [detailed documentation about capabilities in the language reference.](../language/capabilities) +You can find more [detailed documentation about capabilities in the language reference.](../language/capabilities.md) Now, anyone can call the `hello()` method on your `HelloAsset` object by borrowing a reference with your public capability in `/public/Hello`! (Covered in the next section) @@ -317,7 +317,7 @@ In the next section, we look at how capabilities can expand the access a script A script is a very simple transaction type in Cadence that cannot perform any writes to the blockchain and can only read the state of an account or contract. -To execute a script, write a function called `pub fun main()`. +To execute a script, write a function called `access(all) fun main()`. You can click the execute script button to run the script. The result of the script will be printed to the console output. @@ -334,17 +334,15 @@ Open the file `Script1.cdc`. ```cadence Script1.cdc import HelloWorld from 0x01 -pub fun main() { +access(all) fun main() { // Cadence code can get an account's public account object // by using the getAccount() built-in function. let helloAccount = getAccount(0x01) - // Get the public capability from the public path of the owner's account - let helloCapability = helloAccount.getCapability<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) - - // borrow a reference for the capability - let helloReference = helloCapability.borrow() + // Borrow the public capability from the public path of the owner's account + let helloReference = helloAccount.capabilities + .borrow<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) ?? panic("Could not borrow a reference to the hello capability") // The log built-in function logs its argument to stdout. @@ -357,36 +355,31 @@ pub fun main() { ``` Here's what this script does: -1. It fetches the `PublicAccount` object with `getAccount` and assigns it to the variable `helloAccount` -2. Uses the `getCapability` method to get the capability from the `Create Link` transaction -3. Borrows a reference for the capability using the `borrow` method and assigns it to `helloReference` -4. Logs the result of the `hello()` function from `helloReference` to the console. +1. It gets an `Account` reference with `getAccount` and assigns it to the variable `helloAccount` +2. Borrows a reference using the `borrow` method for the capability from the `Create Link` transaction and assigns it to `helloReference` +3. Logs the result of the `hello()` function from `helloReference` to the console. ```cadence let helloAccount = getAccount(0x01) ``` -The `PublicAccount` object is available to anyone in the network for every account, +The `Account` reference is available to anyone in the network for every account, but only has access to a small subset of functions that can be read from the `/public/` domain in an account. -Then, the script gets the capability that was created in `Create Link`. +Then, the script borrows the capability that was created in `Create Link`. ```cadence -// Get the public capability from the public path of the owner's account -let helloCapability = helloAccount.getCapability(/public/HelloAssetTutorial) +// Borrow the public capability from the public path of the owner's account +let helloReference = helloAccount.capabilities + .borrow<&HelloWorld.HelloAsset>(/public/HelloAssetTutorial) + ?? panic("Could not borrow a reference to the hello capability") ``` -To get a capability that is stored in an account, use the `account.getCapability()` function. -This function is available on `AuthAccount`s and on `PublicAccount`s. -`getCapability()` returns a capability for the link at the path that is specified, -also with the type that is specified. -It does not check if the target exists, so the borrow will fail if the capability is invalid. - -After that, the script borrows a reference from the capability. - -```cadence -let helloReference = helloCapability.borrow() -``` +To borrow a capability that is stored in an account, use the `account.capabilities.borrow()` function. +`borrow()` returns a reference to the storage object that the capability targets. +The borrow will fail if the capability does not exist, +the capabilities target storage path does not store a value, +or the value cannot be borrowed with the given type. Then, the script uses the reference to call the `hello()` function and prints the result. @@ -443,11 +436,11 @@ Now that you have completed the tutorial, you have the basic knowledge to write - Interact with resources using both signed transactions and scripts Feel free to modify the smart contract to create different resources, -experiment with the available [account storage API](../language/accounts#account-storage-api), +experiment with the available [account storage API](../language/accounts/storage.mdx), and write new transactions and scripts that execute different functions from your smart contract. -Have a look at the [capability-based access control page](../language/capabilities) +Have a look at the [capability-based access control page](../language/capabilities.md) to find out more about what you can do with capabilities. You're on the right track to building more complex applications with Cadence, -now is a great time to check out the [Cadence Best Practices document](../design-patterns) -and [Anti-patterns document](../anti-patterns) as your applications become more complex. +now is a great time to check out the [Cadence Best Practices document](../design-patterns.md) +and [Anti-patterns document](../anti-patterns.md) as your applications become more complex. diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-1.md b/versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-1.md similarity index 89% rename from versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-1.md rename to versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-1.md index debcce6..7d8a459 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-1.md +++ b/versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-1.md @@ -31,10 +31,10 @@ In this tutorial, we're going to deploy, store, and transfer **Non-Fungible Toke Open the starter code for this tutorial in the Flow Playground: -
https://play.onflow.org/a21087ad-b22c-4981-b49e-17297e916fa6 - +
The tutorial will ask you to take various actions to interact with this code. @@ -128,13 +128,11 @@ We'll start by looking at a basic NFT contract, that adds an NFT to an account. The contract will: 1. Create a smart contract with the NFT resource type. -2. Declare an ID field, a metadata field and an `init()` function in the NFT resource -3. Create an `init()` function for the contract that saves an NFT to an account - -This contract relies on the [account storage API](../language/accounts#authaccount) to save NFTs in the -`AuthAccount` object. +2. Declare an ID field, a metadata field and an initializer in the NFT resource. +3. Create an initializer for the contract that saves an NFT to an account. ---- +This contract relies on the [account storage API](../language/accounts/storage.mdx) +to save NFTs in the account. @@ -155,17 +153,17 @@ Open Account `0x01` to see `BasicNFT.cdc`. ```cadence BasicNFT.cdc -pub contract BasicNFT { +access(all) contract BasicNFT { // Declare the NFT resource type - pub resource NFT { + access(all) resource NFT { // The unique ID that differentiates each NFT - pub let id: UInt64 + access(all) let id: UInt64 // String mapping to hold metadata - pub var metadata: {String: String} + access(all) var metadata: {String: String} - // Initialize both fields in the init function + // Initialize both fields in the initializer init(initID: UInt64) { self.id = initID self.metadata = {} @@ -173,13 +171,13 @@ pub contract BasicNFT { } // Function to create a new NFT - pub fun createNFT(id: UInt64): @NFT { + access(all) fun createNFT(id: UInt64): @NFT { return <-create NFT(initID: id) } // Create a single new NFT and save it to account storage init() { - self.account.save<@NFT>(<-create NFT(initID: 1), to: /storage/BasicNFTPath) + self.account.storage.save(<-create NFT(initID: 1), to: /storage/BasicNFTPath) } } ``` @@ -195,15 +193,14 @@ that can allow the storage of complex file formats and other such data. An NFT could even own other NFTs! This functionality is shown in the next tutorial. -In the contract's `init` function, we create a new NFT object and move it into the account storage. +In the contract's initializer, we create a new NFT object and move it into the account storage. ```cadence // put it in storage -self.account.save<@NFT>(<-create NFT(initID: 1), to: /storage/BasicNFTPath) +self.account.storage.save(<-create NFT(initID: 1), to: /storage/BasicNFTPath) ``` -Here we access the `AuthAccount` object on the account the contract is deployed to and call its `save` method, -specifying `@NFT` as the type it is being saved as. +Here we access the storage object of the account that the contract is deployed to and call its `save` method. We also create the NFT in the same line and pass it as the first argument to `save`. We save it to the `/storage` domain, where objects are meant to be stored. @@ -228,8 +225,8 @@ import BasicNFT from 0x01 // This transaction checks if an NFT exists in the storage of the given account // by trying to borrow from it. If the borrow succeeds (returns a non-nil value), the token exists! transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&BasicNFT.NFT>(from: /storage/BasicNFTPath) != nil { + prepare(acct: auth(BorrowValue) &Account) { + if acct.storage.borrow<&BasicNFT.NFT>(from: /storage/BasicNFTPath) != nil { log("The token exists!") } else { log("No token found!") @@ -268,7 +265,10 @@ import BasicNFT from 0x01 /// to transfer an NFT transaction { - prepare(signer1: AuthAccount, signer2: AuthAccount) { + prepare( + signer1: auth(LoadValue) &Account, + signer2: auth(SaveValue) &Account + ) { // Fill in code here to load the NFT from signer1 // and save it into signer2's storage @@ -311,14 +311,17 @@ import BasicNFT from 0x01 /// to transfer an NFT transaction { - prepare(signer1: AuthAccount, signer2: AuthAccount) { + prepare( + signer1: auth(LoadValue) &Account, + signer2: auth(SaveValue) &Account + ) { // Load the NFT from signer1's account - let nft <- signer1.load<@BasicNFT.NFT>(from: /storage/BasicNFTPath) + let nft <- signer1.storage.load<@BasicNFT.NFT>(from: /storage/BasicNFTPath) ?? panic("Could not load NFT") // Save the NFT to signer2's account - signer2.save<@BasicNFT.NFT>(<-nft, to: /storage/BasicNFTPath) + signer2.storage.save(<-nft, to: /storage/BasicNFTPath) } } diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-2.md b/versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-2.md similarity index 87% rename from versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-2.md rename to versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-2.md index ec6a707..e79e35f 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/05-non-fungible-tokens-2.md +++ b/versioned_docs/version-1.0/tutorial/05-non-fungible-tokens-2.md @@ -30,8 +30,8 @@ a full implementation for **Non-Fungible Tokens (NFTs)**. Open the starter code for this tutorial in the Flow Playground: - https://play.onflow.org/f08e8e0d-d28e-4cbe-8d72-3afe2349c629 @@ -71,7 +71,7 @@ let newNFT <- BasicNFT.createNFT(id: 1) myNFTs[newNFT.id] <- newNFT // Save the NFT to a new storage path -account.save(<-myNFTs, to: /storage/basicNFTDictionary) +account.storage.save(<-myNFTs, to: /storage/basicNFTDictionary) ``` @@ -82,7 +82,7 @@ This example uses a [**Dictionary**: a mutable, unordered collection of key-valu ```cadence // Keys are `Int` // Values are `NFT` -pub let myNFTs: @{Int: NFT} +access(all) let myNFTs: @{Int: NFT} ``` In a dictionary, all keys must have the same type, and all values must have the same type. @@ -112,7 +112,7 @@ This contract expands on the `BasicNFT` we looked at by adding: 4. The `Collection` will declare fields and functions to interact with it, including `ownedNFTs`, `init()`, `withdraw()`, `destroy()`, and other important functions 5. Next, the contract declares functions that create a new NFT (`mintNFT()`) and an empty collection (`createEmptyCollection()`) -7. Finally, the contract declares an `init()` function that initializes the path fields, +7. Finally, the contract declares an initializer that initializes the path fields, creates an empty collection as well as a reference to it, and saves a minter resource to account storage. @@ -137,24 +137,24 @@ It contains what was already in `BasicNFT.cdc` plus additional resource declarat // // Learn more about non-fungible tokens in this tutorial: https://developers.flow.com/cadence/tutorial/non-fungible-tokens-1 -pub contract ExampleNFT { +access(all) contract ExampleNFT { // Declare Path constants so paths do not have to be hardcoded // in transactions and scripts - pub let CollectionStoragePath: StoragePath - pub let CollectionPublicPath: PublicPath - pub let MinterStoragePath: StoragePath + access(all) let CollectionStoragePath: StoragePath + access(all) let CollectionPublicPath: PublicPath + access(all) let MinterStoragePath: StoragePath // Tracks the unique IDs of the NFT - pub var idCount: UInt64 + access(all) var idCount: UInt64 // Declare the NFT resource type - pub resource NFT { + access(all) resource NFT { // The unique ID that differentiates each NFT - pub let id: UInt64 + access(all) let id: UInt64 - // Initialize both fields in the init function + // Initialize both fields in the initializer init(initID: UInt64) { self.id = initID } @@ -164,21 +164,21 @@ pub contract ExampleNFT { // to create public, restricted references to their NFT Collection. // They would use this to publicly expose only the deposit, getIDs, // and idExists fields in their Collection - pub resource interface NFTReceiver { + access(all) resource interface NFTReceiver { - pub fun deposit(token: @NFT) + access(all) fun deposit(token: @NFT) - pub fun getIDs(): [UInt64] + access(all) fun getIDs(): [UInt64] - pub fun idExists(id: UInt64): Bool + access(all) fun idExists(id: UInt64): Bool } // The definition of the Collection resource that // holds the NFTs that a user owns - pub resource Collection: NFTReceiver { + access(all) resource Collection: NFTReceiver { // dictionary of NFT conforming tokens // NFT is a resource type with an `UInt64` ID field - pub var ownedNFTs: @{UInt64: NFT} + access(all) var ownedNFTs: @{UInt64: NFT} // Initialize the NFTs field to an empty collection init () { @@ -189,7 +189,7 @@ pub contract ExampleNFT { // // Function that removes an NFT from the collection // and moves it to the calling context - pub fun withdraw(withdrawID: UInt64): @NFT { + access(all) fun withdraw(withdrawID: UInt64): @NFT { // If the NFT isn't found, the transaction panics and reverts let token <- self.ownedNFTs.remove(key: withdrawID)! @@ -200,7 +200,7 @@ pub contract ExampleNFT { // // Function that takes a NFT as an argument and // adds it to the collections dictionary - pub fun deposit(token: @NFT) { + access(all) fun deposit(token: @NFT) { // add the new token to the dictionary with a force assignment // if there is already a value at that key, it will fail and revert self.ownedNFTs[token.id] <-! token @@ -208,12 +208,12 @@ pub contract ExampleNFT { // idExists checks to see if a NFT // with the given ID exists in the collection - pub fun idExists(id: UInt64): Bool { + access(all) fun idExists(id: UInt64): Bool { return self.ownedNFTs[id] != nil } // getIDs returns an array of the IDs that are in the collection - pub fun getIDs(): [UInt64] { + access(all) fun getIDs(): [UInt64] { return self.ownedNFTs.keys } @@ -223,7 +223,7 @@ pub contract ExampleNFT { } // creates a new empty Collection resource and returns it - pub fun createEmptyCollection(): @Collection { + access(all) fun createEmptyCollection(): @Collection { return <- create Collection() } @@ -231,7 +231,7 @@ pub contract ExampleNFT { // // Function that mints a new NFT with a new ID // and returns it to the caller - pub fun mintNFT(): @NFT { + access(all) fun mintNFT(): @NFT { // create a new NFT var newNFT <- create NFT(initID: self.idCount) @@ -251,10 +251,11 @@ pub contract ExampleNFT { self.idCount = 1 // store an empty NFT Collection in account storage - self.account.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath) + self.account.storage.save(<-self.createEmptyCollection(), to: self.CollectionStoragePath) - // publish a reference to the Collection in storage - self.account.link<&{NFTReceiver}>(self.CollectionPublicPath, target: self.CollectionStoragePath) + // publish a capability to the Collection in storage + let cap = self.account.capabilities.storage.issue<&{NFTReceiver}>(self.CollectionStoragePath) + self.account.capabilities.publish(cap, at: self.CollectionPublicPath) } } ``` @@ -300,10 +301,10 @@ destroy() { } ``` -When the `Collection` resource is created, the `init` function is run +When the `Collection` resource is created, the initializer is run and must explicitly initialize all member variables. This helps prevent issues in some smart contracts where uninitialized fields can cause bugs. -The init function can never run again after this. +The initializer can never run again after this. Here, we initialize the dictionary as a resource type with an empty dictionary. ```cadence @@ -317,7 +318,7 @@ of the keys of the dictionary using the built-in `keys` function. ```cadence // getIDs returns an array of the IDs that are in the collection -pub fun getIDs(): [UInt64] { +access(all) fun getIDs(): [UInt64] { return self.ownedNFTs.keys } ``` @@ -352,7 +353,7 @@ a resource can have more control over the resources it owns than the actual person whose account it is stored in! You'll encounter more fascinating implications of ownership and interoperability -like this as you get deeper into Cadence. +like this as you get deeper into Cadence. Now, back to the tutorial! @@ -361,7 +362,7 @@ Now, back to the tutorial! In the NFT Collection, all the functions and fields are public, but we do not want everyone in the network to be able to call our `withdraw` function. This is where Cadence's second layer of access control comes in. -Cadence utilizes [capability security](../language/capabilities), +Cadence utilizes [capability security](../language/capabilities.md), which means that for any given object, a user is allowed to access a field or method of that object if they either: - Are the owner of the object @@ -373,13 +374,13 @@ is only accessible by its owner. To give external accounts access to the `deposi the `getIDs` function, and the `idExists` function, the owner creates an interface that only includes those fields: ```cadence -pub resource interface NFTReceiver { +access(all) resource interface NFTReceiver { - pub fun deposit(token: @NFT) + access(all) fun deposit(token: @NFT) - pub fun getIDs(): [UInt64] + access(all) fun getIDs(): [UInt64] - pub fun idExists(id: UInt64): Bool + access(all) fun idExists(id: UInt64): Bool } ``` @@ -391,7 +392,7 @@ or they could put in the `/public/` domain of their account so that anyone can a If a user tried to use this capability to call the `withdraw` function, it wouldn't work because it doesn't exist in the interface that was used to create the capability. -The creation of the link and capability is seen in the `ExampleNFT.cdc` contract `init()` function +The creation of the link and capability is seen in the `ExampleNFT.cdc` contract initializer ```cadence // publish a reference to the Collection in storage @@ -424,16 +425,14 @@ Open the script file named `Print 0x01 NFTs`. import ExampleNFT from 0x01 // Print the NFTs owned by account 0x01. -pub fun main() { +access(all) fun main() { // Get the public account object for account 0x01 let nftOwner = getAccount(0x01) - // Find the public Receiver capability for their Collection - let capability = nftOwner.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) - - // borrow a reference from the capability - let receiverRef = capability.borrow() - ?? panic("Could not borrow receiver reference") + // Find the public Receiver capability for their Collection and borrow it + let receiverRef = nftOwner.capabilities + .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) + ?? panic("Could not borrow receiver reference") // Log the NFTs that they own as an array of IDs log("Account 1 NFTs") @@ -491,8 +490,8 @@ transaction { prepare(acct: AuthAccount) { // Get the owner's collection capability and borrow a reference - self.receiverRef = acct.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) - .borrow() + self.receiverRef = acct.capabilities + .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) ?? panic("Could not borrow receiver reference") } @@ -519,7 +518,7 @@ This prints a list of the NFTs that account `0x01` owns. import ExampleNFT from 0x01 // Print the NFTs owned by account 0x01. -pub fun main() { +access(all) fun main() { // Get the public account object for account 0x01 let nftOwner = getAccount(0x01) @@ -562,18 +561,19 @@ import ExampleNFT from 0x01 // to use the NFT contract by creating a new empty collection, // storing it in their account storage, and publishing a capability transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(SaveValue, StorageCapabilities) &Account) { // Create a new empty collection let collection <- ExampleNFT.createEmptyCollection() // store the empty NFT Collection in account storage - acct.save<@ExampleNFT.Collection>(<-collection, to: ExampleNFT.CollectionStoragePath) + acct.storage.save(<-collection, to: ExampleNFT.CollectionStoragePath) log("Collection created for account 2") // create a public capability for the Collection - acct.link<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath) + let cap = acct.capabilities.storage.issue<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionStoragePath) + acct.capabilities.publish(cap, at: ExampleNFT.CollectionPublicPath) log("Capability created") } @@ -601,10 +601,11 @@ transaction { // transferred to the other account let transferToken: @ExampleNFT.NFT - prepare(acct: AuthAccount) { + prepare(acct: auth(BorrowValue) &Account) { // Borrow a reference from the stored collection - let collectionRef = acct.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) + let collectionRef = acct.storage + .borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) ?? panic("Could not borrow a reference to the owner's collection") // Call the withdraw function on the sender's Collection @@ -618,8 +619,8 @@ transaction { // Get the Collection reference for the receiver // getting the public capability and borrowing a reference from it - let receiverRef = recipient.getCapability<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) - .borrow() + let receiverRef = recipient.capabilities + .borrow<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath) ?? panic("Could not borrow receiver reference") // Deposit the NFT in the receivers collection @@ -642,7 +643,7 @@ Execute the script `Print all NFTs` to see the tokens in each account: import ExampleNFT from 0x01 // Print the NFTs owned by accounts 0x01 and 0x02. -pub fun main() { +access(all) fun main() { // Get both public account objects let account1 = getAccount(0x01) diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/06-fungible-tokens.md b/versioned_docs/version-1.0/tutorial/06-fungible-tokens.md similarity index 97% rename from versioned_docs/version-current_0.42/0.42/tutorial/06-fungible-tokens.md rename to versioned_docs/version-1.0/tutorial/06-fungible-tokens.md index 8f65e73..4ac8c8c 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/06-fungible-tokens.md +++ b/versioned_docs/version-1.0/tutorial/06-fungible-tokens.md @@ -236,7 +236,7 @@ init(balance: UFix64) { } ``` -If you remove the initializer from your `ExampleToken` contract, it will cause an error because +If you remove the `init` function from your `ExampleToken` contract, it will cause an error because the balance field is no longer initialized. ### Deposit @@ -396,9 +396,9 @@ access(all) contract BasicToken { return <-create Vault(balance: 30.0) } - // The initializer for the contract. - // All fields in the contract must be initialized at deployment. - // This is just an example of what an implementation could do in the init initializer. + // The init function for the contract. All fields in the contract must + // be initialized at deployment. This is just an example of what + // an implementation could do in the init function. The numbers are arbitrary. init() { // create the Vault with the initial balance and put it in storage // account.save saves an object to the specified `to` path @@ -406,7 +406,7 @@ access(all) contract BasicToken { // The domain must be `storage`, `private`, or `public` // the identifier can be any name let vault <- self.createVault() - self.account.storage.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) + self.account.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) } } ``` @@ -422,7 +422,7 @@ Click the `Deploy` button at the top right of the editor to deploy the code. This deployment stores the contract for the basic fungible token in the selected account (account `0x01`) so that it can be imported into transactions. -A contract's initializer runs at contract creation, and never again afterwards. +A contract's `init` function runs at contract creation, and never again afterwards. In our example, this function stores an instance of the `Vault` object with an initial balance of 30. ```cadence @@ -432,7 +432,7 @@ In our example, this function stores an instance of the `Vault` object with an i // The domain must be `storage`, `private`, or `public` // the identifier can be any name let vault <- self.createVault() -self.account.storage.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) +self.account.save(<-vault, to: /storage/CadenceFungibleTokenTutorialVault) ``` This line saves the new `@Vault` object to storage. @@ -475,10 +475,10 @@ import BasicToken from 0x01 transaction { - prepare(acct: auth(BorrowValue) &Account) { + prepare(acct: AuthAccount) { // withdraw tokens from your vault by borrowing a reference to it // and calling the withdraw function with that reference - let vaultRef = acct.storage.borrow(from: /storage/CadenceFungibleTokenTutorialVault) + let vaultRef = acct.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) ?? panic("Could not borrow a reference to the owner's vault") let temporaryVault <- vaultRef.withdraw(amount: 10.0) @@ -507,7 +507,7 @@ you can borrow a reference directly from an object in storage. ```cadence // Borrow a reference to the stored, private Vault resource -let vaultRef = acct.storage.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) +let vaultRef = acct.borrow<&BasicToken.Vault>(from: /storage/CadenceFungibleTokenTutorialVault) ?? panic("Could not borrow a reference to the owner's vault") ``` @@ -521,7 +521,7 @@ Capabilities allow us to accomplish this safely. --- -Another important feature in Cadence is its utilization of [**Capability-Based Security.**](../language/capabilities) +Another important feature in Cadence is its utilization of [**Capability-Based Security.**](../language/capabilities.md) This feature ensures that while the withdraw function is declared public on the resource, no one except the intended user and those they approve of can withdraw tokens from their vault. @@ -604,7 +604,7 @@ fields private unless it is explicitly needed to be public. This is one of THE MOST COMMON security mistakes that Cadence developers make, so it is vitally important to be aware of this. - See the [Cadence Best Practices document](../anti-patterns#array-or-dictionary-fields-should-be-private) for more details. + See the [Cadence Best Practices document](../anti-patterns.md#array-or-dictionary-fields-should-be-private) for more details. ## Adding Interfaces to Our Fungible Token @@ -630,10 +630,10 @@ This method for authorization can be used in many different ways and further decentralizes the control of the contract. We also store the `VaultMinter` object to `/storage/` -in the initializer in the same way as the vault, but in a different storage path: +in the `init()` function in the same way as the vault, but in a different storage path: ```cadence -self.account.storage<-create VaultMinter(), to: /storage/CadenceFungibleTokenTutorialMinter) +self.account.save(<-create VaultMinter(), to: /storage/CadenceFungibleTokenTutorialMinter) ``` Now is an important time to remind you that account storage not namespaced by contract, @@ -834,7 +834,7 @@ transaction { let vaultA <- ExampleToken.createEmptyVault() // Store the vault in the account storage - acct.storage.save(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) + acct.save<@ExampleToken.Vault>(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) log("Empty Vault stored") @@ -1126,5 +1126,5 @@ From here, you could try to extend the functionality of fungible tokens by makin ## Create a Flow Marketplace --- -Now that you have an understanding of how fungible tokens work on Flow and have a working NFT, you can learn how to create +Now that you have an understanding of how fungible tokens work on Flow and have a working NFT, you can learn how to create a marketplace that uses both fungible tokens and NFTs. Move on to the next tutorial to learn about Marketplaces in Cadence! diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/07-marketplace-setup.md b/versioned_docs/version-1.0/tutorial/07-marketplace-setup.md similarity index 97% rename from versioned_docs/version-current_0.42/0.42/tutorial/07-marketplace-setup.md rename to versioned_docs/version-1.0/tutorial/07-marketplace-setup.md index 2cfc9d6..09eb148 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/07-marketplace-setup.md +++ b/versioned_docs/version-1.0/tutorial/07-marketplace-setup.md @@ -13,8 +13,8 @@ for an example of a production ready marketplace that you can use right now on t --- - Open the starter code for this tutorial in the Flow Playground: - @@ -61,7 +61,7 @@ transaction { log("Created Vault references") // store an empty NFT Collection in account storage - acct.save<@ExampleNFT.Collection>(<-ExampleNFT.createEmptyCollection(), to: /storage/nftTutorialCollection) + acct.storage.save(<-ExampleNFT.createEmptyCollection(), to: /storage/nftTutorialCollection) // publish a capability to the Collection in storage acct.link<&{ExampleNFT.NFTReceiver}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath) @@ -93,7 +93,7 @@ transaction { let vaultA <- ExampleToken.createEmptyVault() // Store the vault in the account storage - acct.save<@ExampleToken.Vault>(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) + acct.storage.save(<-vaultA, to: /storage/CadenceFungibleTokenTutorialVault) // Create a public Receiver capability to the Vault let ReceiverRef = acct.link<&ExampleToken.Vault{ExampleToken.Receiver, ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver, target: /storage/CadenceFungibleTokenTutorialVault) @@ -177,7 +177,7 @@ import ExampleNFT from 0x02 // // Account 0x01: Vault Balance = 40, NFT.id = 1 // Account 0x02: Vault Balance = 20, No NFTs -pub fun main() { +access(all) fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/08-marketplace-compose.md b/versioned_docs/version-1.0/tutorial/08-marketplace-compose.md similarity index 92% rename from versioned_docs/version-current_0.42/0.42/tutorial/08-marketplace-compose.md rename to versioned_docs/version-1.0/tutorial/08-marketplace-compose.md index 3ba9987..f69b9e5 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/08-marketplace-compose.md +++ b/versioned_docs/version-1.0/tutorial/08-marketplace-compose.md @@ -11,8 +11,8 @@ This contract is already deployed to testnet and mainnet and can be used by anyo --- - Open the starter code for this tutorial in the Flow Playground: - @@ -41,7 +41,7 @@ Flow is designed to enable composability because of the way that interfaces, res - [Interfaces](../language/interfaces) allow projects to support any generic type as long as it supports a standard set of functionality specified by an interface. - [Resources](../language/resources) can be passed around and owned by accounts, contracts or even other resources, unlocking different use cases depending on where the resource is stored. -- [Capabilities](../language/capabilities) allow exposing user-defined sets of functionality through special objects that enforce strict security with Cadence's type system. +- [Capabilities](../language/capabilities.md) allow exposing user-defined sets of functionality through special objects that enforce strict security with Cadence's type system. The combination of these allows developers to do more with less, re-using known safe code and design patterns to create new, powerful, and unique interactions! @@ -110,7 +110,7 @@ import ExampleNFT from 0x02 // // Account 0x01: Vault Balance = 40, NFT.id = 1 // Account 0x02: Vault Balance = 20, No NFTs -pub fun main() { +access(all) fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) @@ -219,27 +219,27 @@ import ExampleNFT from 0x02 // // https://github.com/onflow/nft-storefront -pub contract ExampleMarketplace { +access(all) contract ExampleMarketplace { // Event that is emitted when a new NFT is put up for sale - pub event ForSale(id: UInt64, price: UFix64, owner: Address?) + access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) // Event that is emitted when the price of an NFT changes - pub event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) + access(all) event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) // Event that is emitted when a token is purchased - pub event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) + access(all) event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) // Event that is emitted when a seller withdraws their NFT from the sale - pub event SaleCanceled(id: UInt64, seller: Address?) + access(all) event SaleCanceled(id: UInt64, seller: Address?) // Interface that users will publish for their Sale collection // that only exposes the methods that are supposed to be public // - pub resource interface SalePublic { - pub fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) - pub fun idPrice(tokenID: UInt64): UFix64? - pub fun getIDs(): [UInt64] + access(all) resource interface SalePublic { + access(all) fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) + access(all) fun idPrice(tokenID: UInt64): UFix64? + access(all) fun getIDs(): [UInt64] } // SaleCollection @@ -247,7 +247,7 @@ pub contract ExampleMarketplace { // NFT Collection object that allows a user to put their NFT up for sale // where others can send fungible tokens to purchase it // - pub resource SaleCollection: SalePublic { + access(all) resource SaleCollection: SalePublic { /// A capability for the owner's collection access(self) var ownerCollection: Capability<&ExampleNFT.Collection> @@ -278,7 +278,7 @@ pub contract ExampleMarketplace { } // cancelSale gives the owner the opportunity to cancel a sale in the collection - pub fun cancelSale(tokenID: UInt64) { + access(all) fun cancelSale(tokenID: UInt64) { // remove the price self.prices.remove(key: tokenID) self.prices[tokenID] = nil @@ -287,7 +287,7 @@ pub contract ExampleMarketplace { } // listForSale lists an NFT for sale in this collection - pub fun listForSale(tokenID: UInt64, price: UFix64) { + access(all) fun listForSale(tokenID: UInt64, price: UFix64) { pre { self.ownerCollection.borrow()!.idExists(id: tokenID): "NFT to be listed does not exist in the owner's collection" @@ -299,14 +299,14 @@ pub contract ExampleMarketplace { } // changePrice changes the price of a token that is currently for sale - pub fun changePrice(tokenID: UInt64, newPrice: UFix64) { + access(all) fun changePrice(tokenID: UInt64, newPrice: UFix64) { self.prices[tokenID] = newPrice emit PriceChanged(id: tokenID, newPrice: newPrice, owner: self.owner?.address) } // purchase lets a user send tokens to purchase an NFT that is for sale - pub fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) { + access(all) fun purchase(tokenID: UInt64, recipient: Capability<&AnyResource{ExampleNFT.NFTReceiver}>, buyTokens: @ExampleToken.Vault) { pre { self.prices[tokenID] != nil: "No token matching this ID for sale!" @@ -338,18 +338,18 @@ pub contract ExampleMarketplace { } // idPrice returns the price of a specific token in the sale - pub fun idPrice(tokenID: UInt64): UFix64? { + access(all) fun idPrice(tokenID: UInt64): UFix64? { return self.prices[tokenID] } // getIDs returns an array of token IDs that are for sale - pub fun getIDs(): [UInt64] { + access(all) fun getIDs(): [UInt64] { return self.prices.keys } } // createCollection returns a new collection resource to the caller - pub fun createSaleCollection(ownerCollection: Capability<&ExampleNFT.Collection>, + access(all) fun createSaleCollection(ownerCollection: Capability<&ExampleNFT.Collection>, ownerVault: Capability<&AnyResource{ExampleToken.Receiver}>): @SaleCollection { return <- create SaleCollection(ownerCollection: ownerCollection, ownerVault: ownerVault) } @@ -365,7 +365,7 @@ that was explained in [Non-Fungible Tokens](./05-non-fungible-tokens-1.md), with Then, another user can call the `purchase` method, sending their `ExampleToken.Vault` that contains the currency they are using to make the purchase. The buyer also includes a capability to their NFT `ExampleNFT.Collection` so that the purchased token can be immediately deposited into their collection when the purchase is made. -- This marketplace contract stores a capability: `pub let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>`. +- This marketplace contract stores a capability: `access(all) let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>`. The owner of the sale saves a capability to their Fungible Token `Receiver` within the sale. This allows the sale resource to be able to immediately deposit the currency that was used to buy the NFT into the owners `Vault` when a purchase is made. @@ -374,16 +374,16 @@ that was explained in [Non-Fungible Tokens](./05-non-fungible-tokens-1.md), with ```cadence // Event that is emitted when a new NFT is put up for sale - pub event ForSale(id: UInt64, price: UFix64, owner: Address?) + access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) // Event that is emitted when the price of an NFT changes - pub event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) + access(all) event PriceChanged(id: UInt64, newPrice: UFix64, owner: Address?) // Event that is emitted when a token is purchased - pub event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) + access(all) event TokenPurchased(id: UInt64, price: UFix64, seller: Address?, buyer: Address?) // Event that is emitted when a seller withdraws their NFT from the sale - pub event SaleCanceled(id: UInt64, seller: Address?) + access(all) event SaleCanceled(id: UInt64, seller: Address?) ``` This contract has a few new features and concepts that are important to cover: @@ -401,7 +401,7 @@ when getting information about their users' accounts or generating analytics. Events are declared by indicating [the access level](../language/access-control), `event`, and the name and parameters of the event, like a function declaration: ```cadence -pub event ForSale(id: UInt64, price: UFix64, owner: Address?) +access(all) event ForSale(id: UInt64, price: UFix64, owner: Address?) ``` Events cannot modify state at all; they indicate when important actions happen in the smart contract. @@ -422,7 +422,7 @@ As you hopefully understand, [capabilites](../language/capabilities.md) are links to private objects in account storage that specify and expose a subset in the public or private namespace of public or private paths where the Capability is linked. -To create a capability, a user typically uses [the `AuthAccount.link`](../language/accounts.mdx#authaccount) +To create a capability, a user typically uses [the `AuthAccount.link`](../language/accounts) method to create a link to a resource in their private storage, specifying a type to link the capability as: ```cadence @@ -434,7 +434,7 @@ acct.link<&ExampleToken.Vault{ExampleToken.Receiver, ExampleToken.Balance}> (/public/CadenceFungibleTokenTutorialReceiver, target: /storage/CadenceFungibleTokenTutorialVault) ``` -Then, users can get that capability if it was created [in a public path](../language/accounts.mdx#paths), +Then, users can get that capability if it was created [in a public path](../language/accounts/paths), borrow it, and access the functionality that the owner specified. ```cadence @@ -489,7 +489,7 @@ One last piece to consider about capabilities is the decision about when to use This tutorial used to have the `SaleCollection` directly store the NFTs that were for sale, like so: ```cadence -pub resource SaleCollection: SalePublic { +access(all) resource SaleCollection: SalePublic { /// Dictionary of NFT objects for sale /// Maps ID to NFT resource object @@ -506,7 +506,7 @@ then those NFTs are not available to be shown to any app or smart contract that so it is as if the owner doesn't actually own the NFT! In cases like this, we usually recommend using a capability to the main collection so that the main collection can remain unchanged and fully usable by -other smart contracts and apps. This also means that if a for-sale NFT gets transferred by some means other than a purchase, then you need a way to get +other smart contracts and apps. This also means that if a for-sale NFT gets transferred by some means other than a purchase, then you need a way to get rid of the stale listing. That is out of the scope of this tutorial though. Enough explaining! Lets execute some code! @@ -554,7 +554,7 @@ transaction { sale.listForSale(tokenID: 1, price: 10.0) // Store the sale object in the account storage - acct.save(<-sale, to: /storage/NFTSale) + acct.storage.save(<-sale, to: /storage/NFTSale) // Create a public capability to the sale so that others can call its methods acct.link<&ExampleMarketplace.SaleCollection{ExampleMarketplace.SalePublic}>(/public/NFTSale, target: /storage/NFTSale) @@ -585,7 +585,7 @@ import ExampleNFT from 0x02 import ExampleMarketplace from 0x03 // This script prints the NFTs that account 0x01 has for sale. -pub fun main() { +access(all) fun main() { // Get the public account object for account 0x01 let account1 = getAccount(0x01) @@ -713,7 +713,7 @@ import ExampleMarketplace from 0x03 // // Account 1: Vault balance = 50, No NFTs // Account 2: Vault balance = 10, NFT ID=1 -pub fun main() { +access(all) fun main() { // Get the accounts' public account objects let acct1 = getAccount(0x01) let acct2 = getAccount(0x02) @@ -721,13 +721,13 @@ pub fun main() { // Get references to the account's receivers // by getting their public capability // and borrowing a reference from the capability - let acct1ReceiverRef = acct1.getCapability(/public/CadenceFungibleTokenTutorialReceiver) - .borrow<&ExampleToken.Vault{ExampleToken.Balance}>() - ?? panic("Could not borrow acct1 vault reference") + let acct1ReceiverRef = acct1.capabilities + .borrow<&ExampleToken.Vault{ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver) + ?? panic("Could not borrow acct1 vault reference") - let acct2ReceiverRef = acct2.getCapability(/public/CadenceFungibleTokenTutorialReceiver) - .borrow<&ExampleToken.Vault{ExampleToken.Balance}>() - ?? panic("Could not borrow acct2 vault reference") + let acct2ReceiverRef = acct2.capabilities + .borrow<&ExampleToken.Vault{ExampleToken.Balance}>(/public/CadenceFungibleTokenTutorialReceiver) + ?? panic("Could not borrow acct2 vault reference") // Log the Vault balance of both accounts and ensure they are // the correct numbers. @@ -806,14 +806,14 @@ If we wanted to build a central marketplace on-chain, we could use a contract th ```cadence CentralMarketplace.cdc // Marketplace would be the central contract where people can post their sale // references so that anyone can access them -pub contract Marketplace { +access(all) contract Marketplace { // Data structure to store active sales - pub var tokensForSale: {Address: Capability<&SaleCollection>)} + access(all) var tokensForSale: {Address: Capability<&SaleCollection>)} // listSaleCollection lists a users sale reference in the array // and returns the index of the sale so that users can know // how to remove it from the marketplace - pub fun listSaleCollection(collection: Capability<&SaleCollection>) { + access(all) fun listSaleCollection(collection: Capability<&SaleCollection>) { let saleRef = collection.borrow() ?? panic("Invalid sale collection capability") @@ -822,7 +822,7 @@ pub contract Marketplace { // removeSaleCollection removes a user's sale from the array // of sale references - pub fun removeSaleCollection(owner: Address) { + access(all) fun removeSaleCollection(owner: Address) { self.tokensForSale[owner] = nil } diff --git a/docs/tutorial/09-voting.md b/versioned_docs/version-1.0/tutorial/09-voting.md similarity index 100% rename from docs/tutorial/09-voting.md rename to versioned_docs/version-1.0/tutorial/09-voting.md diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/10-resources-compose.md b/versioned_docs/version-1.0/tutorial/10-resources-compose.md similarity index 91% rename from versioned_docs/version-current_0.42/0.42/tutorial/10-resources-compose.md rename to versioned_docs/version-1.0/tutorial/10-resources-compose.md index dd9ca95..e2ddcf3 100644 --- a/versioned_docs/version-current_0.42/0.42/tutorial/10-resources-compose.md +++ b/versioned_docs/version-1.0/tutorial/10-resources-compose.md @@ -8,7 +8,7 @@ In this tutorial, we're going to walk through how resources can own other resour Open the starter code for this tutorial in the Flow Playground: - @@ -92,12 +92,12 @@ The deployed contract should have the following contents: // support even more powerful versions of this. // -pub contract KittyVerse { +access(all) contract KittyVerse { // KittyHat is a special resource type that represents a hat - pub resource KittyHat { - pub let id: Int - pub let name: String + access(all) resource KittyHat { + access(all) let id: Int + access(all) let name: String init(id: Int, name: String) { self.id = id @@ -105,7 +105,7 @@ pub contract KittyVerse { } // An example of a function someone might put in their hat resource - pub fun tipHat(): String { + access(all) fun tipHat(): String { if self.name == "Cowboy Hat" { return "Howdy Y'all" } else if self.name == "Top Hat" { @@ -117,35 +117,35 @@ pub contract KittyVerse { } // Create a new hat - pub fun createHat(id: Int, name: String): @KittyHat { + access(all) fun createHat(id: Int, name: String): @KittyHat { return <-create KittyHat(id: id, name: name) } - pub resource Kitty { + access(all) resource Kitty { - pub let id: Int + access(all) let id: Int // place where the Kitty hats are stored - pub var items: @{String: KittyHat} + access(all) var items: @{String: KittyHat} init(newID: Int) { self.id = newID self.items <- {} } - pub fun getKittyItems(): @{String: KittyHat} { + access(all) fun getKittyItems(): @{String: KittyHat} { var other: @{String:KittyHat} <- {} self.items <-> other return <- other } - pub fun setKittyItems(items: @{String: KittyHat}) { + access(all) fun setKittyItems(items: @{String: KittyHat}) { var other <- items self.items <-> other destroy other } - pub fun removeKittyItem(key: String): @KittyHat? { + access(all) fun removeKittyItem(key: String): @KittyHat? { var removed <- self.items.remove(key: key) return <- removed } @@ -155,7 +155,7 @@ pub contract KittyVerse { } } - pub fun createKitty(): @Kitty { + access(all) fun createKitty(): @Kitty { return <-create Kitty(newID: 1) } @@ -169,7 +169,7 @@ The hats are stored in a variable in the Kitty resource. ```cadence // place where the Kitty hats are stored - pub var items: <-{String: KittyHat} + access(all) var items: <-{String: KittyHat} ``` A Kitty owner can take the hats off the Kitty and transfer them individually. Or the owner can transfer a Kitty that owns a hat, and the hat will go along with the Kitty. @@ -210,7 +210,7 @@ transaction { log("The cat has the hats") // Store the Kitty in storage - acct.save(<-kitty, to: /storage/kitty) + acct.storage.save(<-kitty, to: /storage/kitty) } } ``` @@ -243,7 +243,7 @@ transaction { prepare(acct: AuthAccount) { // Move the Kitty out of storage, which also moves its hat along with it - let kitty <- acct.load<@KittyVerse.Kitty>(from: /storage/kitty) + let kitty <- acct.storage.load<@KittyVerse.Kitty>(from: /storage/kitty) ?? panic("Kitty doesn't exist!") // Take the cowboy hat off the Kitty @@ -259,7 +259,7 @@ transaction { // Move the Kitty to storage, which // also moves its hat along with it. - acct.save(<-kitty, to: /storage/kitty) + acct.storage.save(<-kitty, to: /storage/kitty) } } ``` diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/_category_.json b/versioned_docs/version-1.0/tutorial/_category_.json similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/_category_.json rename to versioned_docs/version-1.0/tutorial/_category_.json diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/deploy_approval_voting.png b/versioned_docs/version-1.0/tutorial/deploy_approval_voting.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/deploy_approval_voting.png rename to versioned_docs/version-1.0/tutorial/deploy_approval_voting.png diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/deploy_basic_token.png b/versioned_docs/version-1.0/tutorial/deploy_basic_token.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/deploy_basic_token.png rename to versioned_docs/version-1.0/tutorial/deploy_basic_token.png diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/deploy_example_token.png b/versioned_docs/version-1.0/tutorial/deploy_example_token.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/deploy_example_token.png rename to versioned_docs/version-1.0/tutorial/deploy_example_token.png diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/deploy_kittyverse.png b/versioned_docs/version-1.0/tutorial/deploy_kittyverse.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/deploy_kittyverse.png rename to versioned_docs/version-1.0/tutorial/deploy_kittyverse.png diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/deploybox.png b/versioned_docs/version-1.0/tutorial/deploybox.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/deploybox.png rename to versioned_docs/version-1.0/tutorial/deploybox.png diff --git a/versioned_docs/version-current_0.42/0.42/tutorial/playground-intro.png b/versioned_docs/version-1.0/tutorial/playground-intro.png similarity index 100% rename from versioned_docs/version-current_0.42/0.42/tutorial/playground-intro.png rename to versioned_docs/version-1.0/tutorial/playground-intro.png diff --git a/versioned_docs/version-current_0.42/0.42/why.md b/versioned_docs/version-1.0/why.md similarity index 100% rename from versioned_docs/version-current_0.42/0.42/why.md rename to versioned_docs/version-1.0/why.md diff --git a/versioned_docs/version-current_0.42/0.42/anti-patterns.md b/versioned_docs/version-current_0.42/0.42/anti-patterns.md deleted file mode 100644 index 726ccf3..0000000 --- a/versioned_docs/version-current_0.42/0.42/anti-patterns.md +++ /dev/null @@ -1,382 +0,0 @@ ---- -title: Cadence Anti-Patterns -sidebar_position: 6 -sidebar_label: 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 - -## 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](./language/references.md) 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/versioned_docs/version-current_0.42/0.42/language/access-control.md b/versioned_docs/version-current_0.42/0.42/language/access-control.md deleted file mode 100644 index 7719ed8..0000000 --- a/versioned_docs/version-current_0.42/0.42/language/access-control.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -title: Access control -sidebar_position: 13 ---- - -Access control allows making certain parts of the program accessible/visible -and making other parts inaccessible/invisible. - -In Flow and Cadence, there are two types of access control: - -1. Access control on objects in account storage using capability security. - - Within Flow, a caller is not able to access an object - unless it owns the object or has a specific reference to that object. - This means that nothing is truly public by default. - Other accounts can not read or write the objects in an account - unless the owner of the account has granted them access - by providing references to the objects. - -2. Access control within contracts and objects - using `pub` and `access` keywords. - - For the explanations of the following keywords, we assume that - the defining type is either a contract, where capability security - doesn't apply, or that the caller would have valid access to the object - governed by capability security. - -The high-level reference-based security (point 1 above) -will be covered in a later section. - -Top-level declarations -(variables, constants, functions, structures, resources, interfaces) -and fields (in structures, and resources) are always only able to be written -to and mutated (modified, such as by indexed assignment or methods like `append`) -in the scope where it is defined (self). - -There are four levels of access control defined in the code that specify where -a declaration can be accessed or called. - -- **Public** or **access(all)** means the declaration - is accessible/visible in all scopes. - - This includes the current scope, inner scopes, and the outer scopes. - - For example, a public field in a type can be accessed using the access syntax - on an instance of the type in an outer scope. - This does not allow the declaration to be publicly writable though. - - An element is made publicly accessible / by any code - by using the `pub` or `access(all)` keywords. - -- **access(account)** means the declaration is only accessible/visible in the - scope of the entire account where it is defined. This means that - other contracts in the account are able to access it, - - An element is made accessible by code in the same account (e.g. other contracts) - by using the `access(account)` keyword. - -- **access(contract)** means the declaration is only accessible/visible in the - scope of the contract that defined it. This means that other types - and functions that are defined in the same contract can access it, - but not other contracts in the same account. - - An element is made accessible by code in the same contract - by using the `access(contract)` keyword. - -- Private or **access(self)** means the declaration is only accessible/visible - in the current and inner scopes. - - For example, an `access(self)` field can only be - accessed by functions of the type is part of, - not by code in an outer scope. - - An element is made accessible by code in the same containing type - by using the `access(self)` keyword. - -**Access level must be specified for each declaration** - -The `(set)` suffix can be used to make variables also publicly writable and mutable. - -To summarize the behavior for variable declarations, constant declarations, and fields: - -| Declaration kind | Access modifier | Read scope | Write scope | Mutate scope | -|:-----------------|:-------------------------|:-----------------------------------------------------|:------------------|:------------------| -| `let` | `priv` / `access(self)` | Current and inner | *None* | Current and inner | -| `let` | `access(contract)` | Current, inner, and containing contract | *None* | Current and inner | -| `let` | `access(account)` | Current, inner, and other contracts in same account | *None* | Current and inner | -| `let` | `pub`,`access(all)` | **All** | *None* | Current and inner | -| `var` | `access(self)` | Current and inner | Current and inner | Current and inner | -| `var` | `access(contract)` | Current, inner, and containing contract | Current and inner | Current and inner | -| `var` | `access(account)` | Current, inner, and other contracts in same account | Current and inner | Current and inner | -| `var` | `pub` / `access(all)` | **All** | Current and inner | Current and inner | -| `var` | `pub(set)` | **All** | **All** | **All** | - -To summarize the behavior for functions: - -| Access modifier | Access scope | -|:-------------------------|:----------------------------------------------------| -| `priv` / `access(self)` | Current and inner | -| `access(contract)` | Current, inner, and containing contract | -| `access(account)` | Current, inner, and other contracts in same account | -| `pub` / `access(all)` | **All** | - -Declarations of structures, resources, events, and [contracts](./contracts.mdx) can only be public. -However, even though the declarations/types are publicly visible, -resources can only be created from inside the contract they are declared in. - -```cadence -// Declare a private constant, inaccessible/invisible in outer scope. -// -access(self) let a = 1 - -// Declare a public constant, accessible/visible in all scopes. -// -pub let b = 2 -``` - -```cadence -// Declare a public struct, accessible/visible in all scopes. -// -pub struct SomeStruct { - - // Declare a private constant field which is only readable - // in the current and inner scopes. - // - access(self) let a: Int - - // Declare a public constant field which is readable in all scopes. - // - pub let b: Int - - // Declare a private variable field which is only readable - // and writable in the current and inner scopes. - // - access(self) var c: Int - - // Declare a public variable field which is not settable, - // so it is only writable in the current and inner scopes, - // and readable in all scopes. - // - pub var d: Int - - // Declare a public variable field which is settable, - // so it is readable and writable in all scopes. - // - pub(set) var e: Int - - // Arrays and dictionaries declared without (set) cannot be - // mutated in external scopes - pub let arr: [Int] - - // The initializer is omitted for brevity. - - // Declare a private function which is only callable - // in the current and inner scopes. - // - access(self) fun privateTest() { - // ... - } - - // Declare a public function which is callable in all scopes. - // - pub fun privateTest() { - // ... - } - - // The initializer is omitted for brevity. - -} - -let some = SomeStruct() - -// Invalid: cannot read private constant field in outer scope. -// -some.a - -// Invalid: cannot set private constant field in outer scope. -// -some.a = 1 - -// Valid: can read public constant field in outer scope. -// -some.b - -// Invalid: cannot set public constant field in outer scope. -// -some.b = 2 - -// Invalid: cannot read private variable field in outer scope. -// -some.c - -// Invalid: cannot set private variable field in outer scope. -// -some.c = 3 - -// Valid: can read public variable field in outer scope. -// -some.d - -// Invalid: cannot set public variable field in outer scope. -// -some.d = 4 - -// Valid: can read publicly settable variable field in outer scope. -// -some.e - -// Valid: can set publicly settable variable field in outer scope. -// -some.e = 5 - -// Invalid: cannot mutate a public field in outer scope. -// -some.f.append(0) - -// Invalid: cannot mutate a public field in outer scope. -// -some.f[3] = 1 - -// Valid: can call non-mutating methods on a public field in outer scope -some.f.contains(0) -``` diff --git a/versioned_docs/version-current_0.42/0.42/language/built-in-functions.mdx b/versioned_docs/version-current_0.42/0.42/language/built-in-functions.mdx deleted file mode 100644 index 82c61fc..0000000 --- a/versioned_docs/version-current_0.42/0.42/language/built-in-functions.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Built-in Functions -sidebar_position: 28 ---- - -## panic -# - -```cadence -fun panic(_ message: String): Never -``` - - Terminates the program unconditionally - and reports a message which explains why the unrecoverable error occurred. - - ```cadence - let optionalAccount: AuthAccount? = // ... - let account = optionalAccount ?? panic("missing account") - ``` - -## assert - -```cadence -fun assert(_ condition: Bool, message: String) -``` - - Terminates the program if the given condition is false, - and reports a message which explains how the condition is false. - Use this function for internal sanity checks. - - The message argument is optional. - -## unsafeRandom - -```cadence -fun unsafeRandom(): UInt64 -``` - - Returns a pseudo-random number. - - NOTE: - Smart contract developers should be mindful about the limitations of unsafeRandom. - The stream of random numbers produced is potentially unsafe in the following two regards: - - 1. The sequence of random numbers is potentially predictable by transactions within the same block - and by other smart contracts calling into your smart contract. - 2. A transaction calling into your smart contract can potentially bias the sequence of random numbers which - your smart contract internally generates. - - We are working towards removing these limitations incrementally. Once these points are addressed, - Flow’s randomness is safe and we will remove the "unsafe" qualifier. - - Nevertheless, there is an additional safety-relevant aspect that developers need to be mindful about: - - * A transaction can atomically revert all its action at any time. Therefore, it is possible for a transaction calling into - your smart contract to post-select favourable results and revert the transaction for unfavourable results. - ([example](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/)) - - This limitation is inherent to any smart contract platform that allows transactions to roll back atomically and cannot be - solved through safe randomness alone. Providing additional Cadence language primitives to simplify this challenge for - developers is on our roadmap as well. Nevertheless, with safe randomness (points 1 and 2 above resolved), developers can prevent - clients from post-select favourable outcomes using approaches such as described in the - [example](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/). - - -## RLP - -RLP (Recursive Length Prefix) serialization allows the encoding of arbitrarily nested arrays of binary data. - -Cadence provides RLP decoding functions in the built-in `RLP` contract, which does not need to be imported. - -- - ```cadence - fun decodeString(_ input: [UInt8]): [UInt8] - ``` - - Decodes an RLP-encoded byte array (called string in the context of RLP). - The byte array should only contain of a single encoded value for a string; if the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. - If any error is encountered while decoding, the program aborts. - - - - ```cadence - fun decodeList(_ input: [UInt8]): [[UInt8]]` - ``` - - Decodes an RLP-encoded list into an array of RLP-encoded items. - Note that this function does not recursively decode, so each element of the resulting array is RLP-encoded data. - The byte array should only contain of a single encoded value for a list; if the encoded value type does not match, or it has trailing unnecessary bytes, the program aborts. - If any error is encountered while decoding, the program aborts. diff --git a/versioned_docs/version-current_0.42/0.42/language/capabilities.md b/versioned_docs/version-current_0.42/0.42/language/capabilities.md deleted file mode 100644 index 8f4da8c..0000000 --- a/versioned_docs/version-current_0.42/0.42/language/capabilities.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -title: Capability-based Access Control -sidebar_position: 20 ---- - -Users will often want to make it so that specific other users or even anyone else -can access certain fields and functions of a stored object. -This can be done by creating a capability. - -As was mentioned before, access to stored objects is governed by the -tenets of [Capability Security](https://en.wikipedia.org/wiki/Capability-based_security). -This means that if an account wants to be able to access another account's -stored objects, it must have a valid capability to that object. - -Capabilities are identified by a path and link to a target path, not directly to an object. -Capabilities are either public (any user can get access), -or private (access to/from the authorized user is necessary). - -Public capabilities are created using public paths, i.e. they have the domain `public`. -After creation they can be obtained from both authorized accounts (`AuthAccount`) -and public accounts (`PublicAccount`). - -Private capabilities are created using private paths, i.e. they have the domain `private`. -After creation they can be obtained from authorized accounts (`AuthAccount`), -but not from public accounts (`PublicAccount`). - -Once a capability is created and obtained, it can be borrowed to get a reference -to the stored object. -When a capability is created, a type is specified that determines as what type -the capability can be borrowed. -This allows exposing and hiding certain functionality of a stored object. - -Capabilities are created using the `link` function of an authorized account (`AuthAccount`): - -- - ```cadence - fun link(_ newCapabilityPath: CapabilityPath, target: Path): Capability? - ``` - - `newCapabilityPath` is the public or private path identifying the new capability. - - `target` is any public, private, or storage path that leads to the object - that will provide the functionality defined by this capability. - - `T` is the type parameter for the capability type. - A type argument for the parameter must be provided explicitly. - - The type parameter defines how the capability can be borrowed, - i.e., how the stored value can be accessed. - - The link function returns `nil` if a link for the given capability path already exists, - or the newly created capability if not. - - It is not necessary for the target path to lead to a valid object; - the target path could be empty, or could lead to an object - which does not provide the necessary type interface: - - 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. - - The link is latent. - The target value might be stored after the link is created, - and the target value might be moved out after the link has been created. - -Capabilities can be removed using the `unlink` function of an authorized account (`AuthAccount`): - -- - ```cadence - fun unlink(_ path: CapabilityPath) - ``` - - `path` is the public or private path identifying the capability that should be removed. - -To get the target path for a capability, the `getLinkTarget` function -of an authorized account (`AuthAccount`) or public account (`PublicAccount`) can be used: - - -- - ```cadence - fun getLinkTarget(_ path: CapabilityPath): Path? - ``` - - `path` is the public or private path identifying the capability. - The function returns the link target path, - if a capability exists at the given path, - or `nil` if it does not. - -Existing capabilities can be obtained by using the `getCapability` function -of authorized accounts (`AuthAccount`) and public accounts (`PublicAccount`): - -- - ```cadence - fun getCapability(_ at: CapabilityPath): Capability - ``` - - For public accounts, the function returns a capability - if the given path is public. - It is not possible to obtain private capabilities from public accounts. - If the path is private or a storage path, the function returns `nil`. - - For authorized accounts, the function returns a capability - if the given path is public or private. - If the path is a storage path, the function returns `nil`. - - `T` is the type parameter that specifies how the capability can be borrowed. - The type argument is optional, i.e. it need not be provided. - -The `getCapability` function does **not** check if the target exists. -The link is latent. -The `check` function of the capability can be used to check if the target currently exists and could be borrowed, - -- - ```cadence - fun check(): Bool - ``` - - `T` is the type parameter for the reference type. - A type argument for the parameter must be provided explicitly. - - The function returns true if the capability currently targets an object - that satisfies the given type, i.e. could be borrowed using the given type. - -Finally, the capability can be borrowed to get a reference to the stored object. -This can be done using the `borrow` function of the capability: - -- - ```cadence - fun borrow(): T? - ``` - - The function returns a reference to the object targeted by the capability, - provided it can be borrowed using the given type. - - `T` is the type parameter for the reference type. - If the function is called on a typed capability, the capability's type is used when borrowing. - If the capability is untyped, a type argument must be provided explicitly in the call to `borrow`. - - The function returns `nil` when the targeted path is empty, i.e. nothing is stored under it. - When the requested type exceeds what is allowed by the capability (or any interim capabilities), - execution will abort with an error. - -```cadence -// Declare a resource interface named `HasCount`, that has a field `count` -// -resource interface HasCount { - count: Int -} - -// Declare a resource named `Counter` that conforms to `HasCount` -// -resource Counter: HasCount { - pub var count: Int - - pub init(count: Int) { - self.count = count - } - - pub fun increment(by amount: Int) { - self.count = self.count + amount - } -} - -// In this example an authorized account is available through the constant `authAccount`. - -// Create a new instance of the resource type `Counter` -// and save it in the storage of the account. -// -// The path `/storage/counter` is used to refer to the stored value. -// Its identifier `counter` was chosen freely and could be something else. -// -authAccount.save(<-create Counter(count: 42), to: /storage/counter) - -// Create a public capability that allows access to the stored counter object -// as the type `{HasCount}`, i.e. only the functionality of reading the field -// -authAccount.link<&{HasCount}>(/public/hasCount, target: /storage/counter) -``` - -To get the published portion of an account, the `getAccount` function can be used. - -Imagine that the next example is from a different account as before. - -```cadence - -// Get the public account for the address that stores the counter -// -let publicAccount = getAccount(0x1) - -// Get a capability for the counter that is made publicly accessible -// through the path `/public/hasCount`. -// -// Use the type `&{HasCount}`, a reference to some object that provides the functionality -// of interface `HasCount`. This is the type that the capability can be borrowed as -// (it was specified in the call to `link` above). -// See the example below for borrowing using the type `&Counter`. -// -// After the call, the declared constant `countCap` has type `Capability<&{HasCount}>`, -// a capability that results in a reference that has type `&{HasCount}` when borrowed. -// -let countCap = publicAccount.getCapability<&{HasCount}>(/public/hasCount) - -// Borrow the capability to get a reference to the stored counter. -// -// This borrow succeeds, i.e. the result is not `nil`, -// it is a valid reference, because: -// -// 1. Dereferencing the path chain results in a stored object -// (`/public/hasCount` links to `/storage/counter`, -// and there is an object stored under `/storage/counter`) -// -// 2. The stored value is a subtype of the requested type `{HasCount}` -// (the stored object has type `Counter` which conforms to interface `HasCount`) -// -let countRef = countCap.borrow()! - -countRef.count // is `42` - -// Invalid: The `increment` function is not accessible for the reference, -// because it has the type `&{HasCount}`, which does not expose an `increment` function, -// only a `count` field -// -countRef.increment(by: 5) - -// Again, attempt to get a get a capability for the counter, but use the type `&Counter`. -// -// Getting the capability succeeds, because it is latent, but borrowing fails -// (the result s `nil`), because the capability was created/linked using the type `&{HasCount}`: -// -// The resource type `Counter` implements the resource interface `HasCount`, -// so `Counter` is a subtype of `{HasCount}`, but the capability only allows -// borrowing using unauthorized references of `{HasCount}` (`&{HasCount}`) -// instead of authorized references (`auth &{HasCount}`), -// so users of the capability are not allowed to borrow using subtypes, -// and they can't escalate the type by casting the reference either. -// -// This shows how parts of the functionality of stored objects -// can be safely exposed to other code -// -let countCapNew = publicAccount.getCapability<&Counter>(/public/hasCount) -let counterRefNew = countCapNew.borrow() - -// `counterRefNew` is `nil`, the borrow failed - -// Invalid: Cannot access the counter object in storage directly, -// the `borrow` function is not available for public accounts -// -let counterRef2 = publicAccount.borrow<&Counter>(from: /storage/counter) -``` - -The address of a capability can be obtained from the `address` field of the capability: - -- - ```cadence - let address: Address - ``` - - The address of the capability. diff --git a/versioned_docs/version-current_0.42/0.42/language/contracts.mdx b/versioned_docs/version-current_0.42/0.42/language/contracts.mdx deleted file mode 100644 index 5c8beed..0000000 --- a/versioned_docs/version-current_0.42/0.42/language/contracts.mdx +++ /dev/null @@ -1,496 +0,0 @@ ---- -title: Contracts -sidebar_position: 22 ---- - -A contract in Cadence is a collection of type definitions -of interfaces, structs, resources, data (its state), and code (its functions) -that lives in the contract storage area of an account in Flow. - -Contracts are where all composite types like structs, resources, -events, and interfaces for these types in Cadence have to be defined. -Therefore, an object of one of these types cannot exist -without having been defined in a deployed Cadence contract. - -Contracts can be created, updated, and removed using the `contracts` -object of [authorized accounts](./accounts.mdx). -This functionality is covered in the [next section](#deploying-updating-and-removing-contracts) - -Contracts are types. -They are similar to composite types, but are stored differently than -structs or resources and cannot be used as values, copied, or moved -like resources or structs. - -Contracts stay in an account's contract storage area -and can only be added, updated, or removed by the account owner with special commands. - -Contracts are declared using the `contract` keyword. The keyword is followed -by the name of the contract. - -```cadence -pub contract SomeContract { - // ... -} -``` - -Contracts cannot be nested in each other. - -```cadence -pub contract Invalid { - - // Invalid: Contracts cannot be nested in any other type. - // - pub contract Nested { - // ... - } -} -``` - -One of the simplest forms of a contract would just be one with a state field, -a function, and an `init` function that initializes the field: - -```cadence -pub contract HelloWorld { - - // Declare a stored state field in HelloWorld - // - pub let greeting: String - - // Declare a function that can be called by anyone - // who imports the contract - // - pub fun hello(): String { - return self.greeting - } - - init() { - self.greeting = "Hello World!" - } -} -``` - -This contract could be deployed to an account and live permanently -in the contract storage. Transactions and other contracts -can interact with contracts by importing them at the beginning -of a transaction or contract definition. - -Anyone could call the above contract's `hello` function by importing -the contract from the account it was deployed to and using the imported -object to call the hello function. - -```cadence -import HelloWorld from 0x42 - -// Invalid: The contract does not know where hello comes from -// -log(hello()) // Error - -// Valid: Using the imported contract object to call the hello -// function -// -log(HelloWorld.hello()) // prints "Hello World!" - -// Valid: Using the imported contract object to read the greeting -// field. -log(HelloWorld.greeting) // prints "Hello World!" - -// Invalid: Cannot call the init function after the contract has been created. -// -HelloWorld.init() // Error -``` - -There can be any number of contracts per account -and they can include an arbitrary amount of data. -This means that a contract can have any number of fields, functions, and type definitions, -but they have to be in the contract and not another top-level definition. - -```cadence -// Invalid: Top-level declarations are restricted to only be contracts -// or contract interfaces. Therefore, all of these would be invalid -// if they were deployed to the account contract storage and -// the deployment would be rejected. -// -pub resource Vault {} -pub struct Hat {} -pub fun helloWorld(): String {} -let num: Int -``` - -Another important feature of contracts is that instances of resources and events -that are declared in contracts can only be created/emitted within functions or types -that are declared in the same contract. - -It is not possible create instances of resources and events outside the contract. - -The contract below defines a resource interface `Receiver` and a resource `Vault` -that implements that interface. The way this example is written, -there is no way to create this resource, so it would not be usable. - -```cadence -// Valid -pub contract FungibleToken { - - pub resource interface Receiver { - - pub balance: Int - - pub fun deposit(from: @{Receiver}) { - pre { - from.balance > 0: - "Deposit balance needs to be positive!" - } - post { - self.balance == before(self.balance) + before(from.balance): - "Incorrect amount removed" - } - } - } - - pub resource Vault: Receiver { - - // keeps track of the total balance of the accounts tokens - pub var balance: Int - - init(balance: Int) { - self.balance = balance - } - - // withdraw subtracts amount from the vaults balance and - // returns a vault object with the subtracted balance - pub fun withdraw(amount: Int): @Vault { - self.balance = self.balance - amount - return <-create Vault(balance: amount) - } - - // deposit takes a vault object as a parameter and adds - // its balance to the balance of the Account's vault, then - // destroys the sent vault because its balance has been consumed - pub fun deposit(from: @{Receiver}) { - self.balance = self.balance + from.balance - destroy from - } - } -} -``` - -If a user tried to run a transaction that created an instance of the `Vault` type, -the type checker would not allow it because only code in the `FungibleToken` -contract can create new `Vault`s. - -```cadence -import FungibleToken from 0x42 - -// Invalid: Cannot create an instance of the `Vault` type outside -// of the contract that defines `Vault` -// -let newVault <- create FungibleToken.Vault(balance: 10) -``` - -The contract would have to either define a function that creates new -`Vault` instances or use its `init` function to create an instance and -store it in the owner's account storage. - -This brings up another key feature of contracts in Cadence. Contracts -can interact with its account's `storage` and `published` objects to store -resources, structs, and references. -They do so by using the special `self.account` object that is only accessible within the contract. - -Imagine that these were declared in the above `FungibleToken` contract. - -```cadence - - pub fun createVault(initialBalance: Int): @Vault { - return <-create Vault(balance: initialBalance) - } - - init(balance: Int) { - let vault <- create Vault(balance: 1000) - self.account.save(<-vault, to: /storage/initialVault) - } -``` - -Now, any account could call the `createVault` function declared in the contract -to create a `Vault` object. -Or the owner could call the `withdraw` function on their own `Vault` to send new vaults to others. - -```cadence -import FungibleToken from 0x42 - -// Valid: Create an instance of the `Vault` type by calling the contract's -// `createVault` function. -// -let newVault <- create FungibleToken.createVault(initialBalance: 10) -``` - -## Account access - -Contracts have the implicit field `let account: AuthAccount`, -which is the account in which the contract is deployed too. -This gives the contract the ability to e.g. read and write to the account's storage. - -## Deploying, Updating, and Removing Contracts - -In order for a contract to be used in Cadence, it needs to be deployed to an account. -The deployed contracts of an account can be accessed through the `contracts` object. - -### Deployed Contracts - -Accounts store "deployed contracts", that is, the code of the contract: - -```cadence -pub struct DeployedContract { - /// The address of the account where the contract is deployed at. - pub let address: Address - - /// The name of the contract. - pub let name: String - - /// The code of the contract. - pub let code: [UInt8] - - /// Returns an array of `Type` objects representing all the public type declarations in this contract - /// (e.g. structs, resources, enums). - /// - /// For example, given a contract - /// ``` - /// contract Foo { - /// pub struct Bar {...} - /// pub resource Qux {...} - /// } - /// ``` - /// then `.publicTypes()` will return an array equivalent to the expression `[Type(), Type()]` - pub fun publicTypes(): [Type] -} -``` - -Note that this is not the contract instance that can be acquired by importing it. - -### Deploying a New Contract - -A new contract can be deployed to an account using the `add` function: - - ```cadence - fun add( - name: String, - code: [UInt8], - ... contractInitializerArguments - ): DeployedContract - ``` - - Adds the given contract to the account. - - The `code` parameter is the UTF-8 encoded representation of the source code. - The code must contain exactly one contract or contract interface, - which must have the same name as the `name` parameter. - - All additional arguments that are given are passed further to the initializer - of the contract that is being deployed. - - Fails if a contract/contract interface with the given name already exists in the account, - if the given code does not declare exactly one contract or contract interface, - or if the given name does not match the name of the contract/contract interface declaration in the code. - - Returns the [deployed contract](#deployed-contracts). - -For example, assuming the following contract code should be deployed: - -```cadence -pub contract Test { - pub let message: String - - init(message: String) { - self.message = message - } -} -``` - -The contract can be deployed as follows: - -```cadence -// Decode the hex-encoded source code into a byte array -// using the built-in function `decodeHex`. -// -// (The ellipsis ... indicates the remainder of the string) -// -let code = "70756220636f6e...".decodeHex() - -// `code` has type `[UInt8]` - -let signer: AuthAccount = ... -signer.contracts.add( - name: "Test", - code: code, - message: "I'm a new contract in an existing account" -) -``` - -### Updating a Deployed Contract - - - -🚧 Status: Updating contracts is **experimental**. - -Updating contracts is currently limited to maintain data consistency. -[Certain restrictions are imposed](./contract-updatability.md). - - - -A deployed contract can be updated using the `update__experimental` function: - - ```cadence - fun update__experimental(name: String, code: [UInt8]): DeployedContract - ``` - - Updates the code for the contract/contract interface in the account. - - The `code` parameter is the UTF-8 encoded representation of the source code. - The code must contain exactly one contract or contract interface, - which must have the same name as the `name` parameter. - - Does **not** run the initializer of the contract/contract interface again. - The contract instance in the world state stays as is. - - Fails if no contract/contract interface with the given name exists in the account, - if the given code does not declare exactly one contract or contract interface, - or if the given name does not match the name of the contract/contract interface declaration in the code. - - Returns the [deployed contract](#deployed-contracts) for the updated contract. - -For example, assuming that a contract named `Test` is already deployed to the account -and it should be updated with the following contract code: - -```cadence -pub contract Test { - pub let message: String - - init(message: String) { - self.message = message - } -} -``` - -The contract can be updated as follows: - -```cadence -// Decode the hex-encoded source code into a byte array -// using the built-in function `decodeHex`. -// -// (The ellipsis ... indicates the remainder of the string) -// -let code = "70756220636f6e...".decodeHex() - -// `code` has type `[UInt8]` - -let signer: AuthAccount = ... -signer.contracts.update__experimental(name: "Test", code: code) -``` - -Updating a contract does **not** currently change any existing stored data. -Only the code of the contract is updated. - -### Getting a Deployed Contract - -A deployed contract can be gotten from an account using the `get` function: - - ```cadence - fun get(name: String): DeployedContract? - ``` - - Returns the [deployed contract](#deployed-contracts) for the contract/contract interface with the given name in the account, if any. - - Returns `nil` if no contract/contract interface with the given name exists in the account. - -For example, assuming that a contract named `Test` is deployed to an account, the contract can be retrieved as follows: - -```cadence -let signer: AuthAccount = ... -let contract = signer.contracts.get(name: "Test") -``` - -### Borrowing a Deployed Contract - -In contrast to a static contract import `import T from 0x1`, -which will always perform an import of a type, -contracts can be "borrowed" to effectively perform a dynamic import dependent on a specific execution path. - -A reference to a deployed contract contract can obtained using the `borrow` function: - - ```cadence - fun borrow(name: String): T? - ``` - - This returns a reference to the contract value stored with that name on the account, - if it exists, and if it has the provided type `T`. - - Returns `nil` if no contract/contract interface with the given name exists in the account. - -For example, assuming that a contract named `Test` which conforms to the `TestInterface` interface is deployed to an account, the contract can be retrieved as follows: - -```cadence -let signer: AuthAccount = ... -let contract: &TestInterface = signer.contracts.borrow<&TestInterface>(name: "Test") - -### Removing a Deployed Contract - -A deployed contract can be removed from an account using the `remove` function: - -```cadence -fun remove(name: String): DeployedContract? -``` - - Removes the contract/contract interface from the account which has the given name, if any. - - Returns the removed [deployed contract](#deployed-contracts), if any. - - Returns `nil` if no contract/contract interface with the given name exist in the account. - -For example, assuming that a contract named `Test` is deployed to an account, the contract can be removed as follows: - -```cadence -let signer: AuthAccount = ... -let contract = signer.contracts.remove(name: "Test") -``` - -## Contract Interfaces - -Like composite types, contracts can have interfaces that specify rules -about their behavior, their types, and the behavior of their types. - -Contract interfaces have to be declared globally. Declarations -cannot be nested in other types. - -If a contract interface declares a concrete type, implementations of it -must also declare the same concrete type conforming to the type requirement. - -If a contract interface declares an interface type, the implementing contract -does not have to also define that interface. They can refer to that nested -interface by saying `{ContractInterfaceName}.{NestedInterfaceName}` - -```cadence -// Declare a contract interface that declares an interface and a resource -// that needs to implement that interface in the contract implementation. -// -pub contract interface InterfaceExample { - - // Implementations do not need to declare this - // They refer to it as InterfaceExample.NestedInterface - // - pub resource interface NestedInterface {} - - // Implementations must declare this type - // - pub resource Composite: NestedInterface {} -} - -pub contract ExampleContract: InterfaceExample { - - // The contract doesn't need to redeclare the `NestedInterface` interface - // because it is already declared in the contract interface - - // The resource has to refer to the resource interface using the name - // of the contract interface to access it - // - pub resource Composite: InterfaceExample.NestedInterface { - } -} -``` diff --git a/versioned_docs/version-current_0.42/0.42/language/transactions.md b/versioned_docs/version-current_0.42/0.42/language/transactions.md deleted file mode 100644 index d84f179..0000000 --- a/versioned_docs/version-current_0.42/0.42/language/transactions.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: Transactions -sidebar_position: 26 ---- - -Transactions are objects that are signed by one or more [accounts](./accounts.mdx) -and are sent to the chain to interact with it. - -Transactions are structured as such: - -First, the transaction can import any number of types from external accounts -using the import syntax. - -```cadence -import FungibleToken from 0x01 -``` - -The body is declared using the `transaction` keyword and its contents -are contained in curly braces. - -Next is the body of the transaction, -which first contains local variable declarations that are valid -throughout the whole of the transaction. - -```cadence -transaction { - // transaction contents - let localVar: Int - - ... -} -``` - -Then, four optional main phases: -Preparation, preconditions, execution, and postconditions, in that order. -The preparation and execution phases are blocks of code that execute sequentially. - -The following empty Cadence transaction contains no logic, -but demonstrates the syntax for each phase, in the order these phases will be executed: - -```cadence -transaction { - prepare(signer1: AuthAccount, signer2: AuthAccount) { - // ... - } - - pre { - // ... - } - - execute { - // ... - } - - post { - // ... - } -} -``` - -Although optional, each phase serves a specific purpose when executing a transaction -and it is recommended that developers use these phases when creating their transactions. -The following will detail the purpose of and how to use each phase. - -## Transaction Parameters - -Transactions may declare parameters. -Transaction parameters are declared like function parameters. -The arguments for the transaction are passed in the sent transaction. - -Transaction parameters are accessible in all phases. - -```cadence -// Declare a transaction which has one parameter named `amount` -// that has the type `UFix64` -// -transaction(amount: UFix64) { - -} -``` - -## Prepare phase - -The `prepare` phase is used when access to the private `AuthAccount` object -of **signing accounts** is required for your transaction. - -Direct access to signing accounts is **only possible inside the `prepare` phase**. - -For each signer of the transaction the signing account is passed as an argument to the `prepare` phase. -For example, if the transaction has three signers, -the prepare **must** have three parameters of type `AuthAccount`. - -```cadence - prepare(signer1: AuthAccount) { - // ... - } -``` - -As a best practice, only use the `prepare` phase to define and execute logic that requires access -to the `AuthAccount` objects of signing accounts, -and *move all other logic elsewhere*. -Modifications to accounts can have significant implications, -so keep this phase clear of unrelated logic to ensure users of your contract are able to easily read -and understand logic related to their private account objects. - -The prepare phase serves a similar purpose as the initializer of a contract/resource/structure. - -For example, if a transaction performs a token transfer, put the withdrawal in the `prepare` phase, -as it requires access to the account storage, but perform the deposit in the `execute` phase. - -`AuthAccount` objects have the permissions -to read from and write to the `/storage/` and `/private/` areas -of the account, which cannot be directly accessed anywhere else. -They also have the permission to create and delete capabilities that -use these areas. - -## Pre Phase - -The `pre` phase is executed after the `prepare` phase, and is used for checking -if explicit conditions hold before executing the remainder of the transaction. -A common example would be checking requisite balances before transferring tokens between accounts. - -```cadence -pre { - sendingAccount.balance > 0 -} -``` - -If the `pre` phase throws an error, or does not return `true` the remainder of the transaction -is not executed and it will be completely reverted. - -## Execute Phase - -The `execute` phase does exactly what it says, it executes the main logic of the transaction. -This phase is optional, but it is a best practice to add your main transaction logic in the section, -so it is explicit. - -```cadence -execute { - // Invalid: Cannot access the authorized account object, - // as `account1` is not in scope - let resource <- account1.load<@Resource>(from: /storage/resource) - destroy resource - - // Valid: Can access any account's public Account object - let publicAccount = getAccount(0x03) -} -``` - -You **may not** access private `AuthAccount` objects in the `execute` phase, -but you may get an account's `PublicAccount` object, -which allows reading and calling methods on objects -that an account has published in the public domain of its account (resources, contract methods, etc.). - -## Post Phase - -Statements inside of the `post` phase are used -to verify that your transaction logic has been executed properly. -It contains zero or more condition checks. - -For example, a transfer transaction might ensure that the final balance has a certain value, -or e.g. it was incremented by a specific amount. - -```cadence -post { - result.balance == 30: "Balance after transaction is incorrect!" -} -``` - -If any of the condition checks result in `false`, the transaction will fail and be completely reverted. - -Only condition checks are allowed in this section. -No actual computation or modification of values is allowed. - -**A Note about `pre` and `post` Phases** - -Another function of the `pre` and `post` phases is to help provide information -about how the effects of a transaction on the accounts and resources involved. -This is essential because users may want to verify what a transaction does before submitting it. -`pre` and `post` phases provide a way to introspect transactions before they are executed. - -For example, in the future the phases could be analyzed and interpreted to the user -in the software they are using, -e.g. "this transaction will transfer 30 tokens from A to B. -The balance of A will decrease by 30 tokens and the balance of B will increase by 30 tokens." - -## Summary - -Cadence transactions use phases to make the transaction's code / intent more readable -and to provide a way for developer to separate potentially 'unsafe' account -modifying code from regular transaction logic, -as well as provide a way to check for error prior / after transaction execution, -and abort the transaction if any are found. - -The following is a brief summary of how to use the `prepare`, `pre`, `execute`, -and `post` phases in a Cadence transaction. - -```cadence -transaction { - prepare(signer1: AuthAccount) { - // Access signing accounts for this transaction. - // - // Avoid logic that does not need access to signing accounts. - // - // Signing accounts can't be accessed anywhere else in the transaction. - } - - pre { - // Define conditions that must be true - // for this transaction to execute. - } - - execute { - // The main transaction logic goes here, but you can access - // any public information or resources published by any account. - } - - post { - // Define the expected state of things - // as they should be after the transaction executed. - // - // Also used to provide information about what changes - // this transaction will make to accounts in this transaction. - } -} -``` diff --git a/versioned_sidebars/version-1.0-sidebars.json b/versioned_sidebars/version-1.0-sidebars.json new file mode 100644 index 0000000..1f21c96 --- /dev/null +++ b/versioned_sidebars/version-1.0-sidebars.json @@ -0,0 +1,4 @@ +{ + "docSidebar": [{ "type": "autogenerated", "dirName": "." }] +} + diff --git a/versioned_sidebars/version-current_0.42-sidebars.json b/versioned_sidebars/version-current_0.42-sidebars.json deleted file mode 100644 index 2c6335a..0000000 --- a/versioned_sidebars/version-current_0.42-sidebars.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "docSidebar": [{ "type": "autogenerated", "dirName": "0.42" }] -} - diff --git a/versions.json b/versions.json index bbb5af8..9a369ed 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ [ - "current_0.42" + "1.0" ] From f6ff4532847cabb2d73af7b1d1eca2d4dc55fe4d Mon Sep 17 00:00:00 2001 From: Jan Bernatik Date: Wed, 8 Nov 2023 16:46:50 +0100 Subject: [PATCH 2/2] fixing "Read the Documentation" link on the "learn" page --- src/pages/learn.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/learn.js b/src/pages/learn.js index 1c82732..aef6411 100644 --- a/src/pages/learn.js +++ b/src/pages/learn.js @@ -24,7 +24,7 @@ export default function Learn() {
  • - 📕 Read the documentation + 📕 Read the documentation