diff --git a/docs/tutorial/09-voting.md b/docs/tutorial/09-voting.md index 782c50a4..607e3453 100644 --- a/docs/tutorial/09-voting.md +++ b/docs/tutorial/09-voting.md @@ -1,67 +1,40 @@ --- -title: 9. Voting Contract +title: Voting Contract --- In this tutorial, we're going to deploy a contract that allows users to vote on multiple proposals that a voting administrator controls. ---- - -:::info - -Open the starter code for this tutorial in the Flow Playground: - -https://play.flow.com/e8e2af39-370d-4a52-9f0b-bfb3b12c7eff - -The tutorial will be asking you to take various actions to interact with this code. - -::: - -:::info[Action] +## Objectives -Instructions that require you to take action are always included in a callout box like this one. -These highlighted actions are all that you need to do to get your code running, -but reading the rest is necessary to understand the language's design. +With the advent of blockchain technology and smart contracts, it has become popular to try to create decentralized voting mechanisms that allow large groups of users to vote completely onchain. This tutorial provides a trivial example of how this might be achieved by using a resource-oriented programming model. -::: +We'll take you through these steps to get comfortable with the Voting contract: -With the advent of blockchain technology and smart contracts, -it has become popular to try to create decentralized voting mechanisms that allow large groups of users to vote completely on chain. -This tutorial will provide a trivial example for how this might be achieved by using a resource-oriented programming model. - -We'll take you through these steps to get comfortable with the Voting contract. - -1. Deploy the contract to account `0x06` -2. Create proposals for users to vote on +1. Deploy the contract to account `0x06`. +2. Create proposals for users to vote on. 3. Use a transaction with multiple signers to directly transfer the `Ballot` resource to another account. -4. Record and cast your vote in the central Voting contract -5. Read the results of the vote +4. Record and cast your vote in the central Voting contract. +5. Read the results of the vote. -Before proceeding with this tutorial, we highly recommend following the instructions in [Getting Started](./01-first-steps.md) -and [Hello, World!](./02-hello-world.md) to learn how to use the Playground tools and to learn the fundamentals of Cadence. +Before proceeding with this tutorial, we highly recommend following the instructions in [First Steps] and [Hello World] to learn how to use the Playground tools and to learn the fundamentals of Cadence. -## A Voting Contract in Cadence +## Getting started -In this contract, a Ballot is represented as a resource. +Open the starter code for this tutorial in the Flow Playground: [https://play.flow.com/e8e2af39-370d-4a52-9f0b-bfb3b12c7eff]. -An administrator can give Ballots to other accounts, then those accounts mark which proposals they vote for -and submit the Ballot to the central smart contract to have their votes recorded. +The tutorial will prompt you to take various actions to interact with this code. -Using a [resource](../language/resources.mdx) type is logical for this application, because if a user wants to delegate their vote, -they can send that Ballot to another account, and the use case of voting ballots benefits from the uniqueness and existence guarantees -inherent to resources. +## A voting contract in Cadence -## Write the Contract +In this contract, a ballot is represented as a resource. -Time to see the contract we'll be working with: +An administrator can give ballots to other accounts, then those accounts mark which proposals they vote for and submit the ballot to the central smart contract to have their votes recorded. -:::info[Action] +Using a [resource] type is logical for this application because if a user wants to delegate their vote, they can send that ballot to another account, and the use case of voting ballots benefits from the uniqueness and existence guarantees inherent to resources. -1. Open Contract 1 - the `ApprovalVoting` contract. +## Writing the contract -::: +To see the contract we'll be working with, open Contract 1 — the `ApprovalVoting` contract. The contract should have the following contents: @@ -97,14 +70,14 @@ contract ApprovalVoting { // Entitlement: Admin entitlement that restricts the privileged fields // of the Admin resource - // Resource: Ballot resource that is issued to users. - // When a user gets a Ballot object, they call the `vote` function - // to include their votes for each proposal, and then cast it in the smart contract + // Resource: ballot resource that is issued to users. + // When a user gets a ballot object, they call the `vote` function + // to include their votes for each proposal, and then cast them in the smart contract // using the `cast` function to have their vote included in the polling - // Remember to track which proposals a user has voted yes for in the Ballot resource - // and remember to include proper pre and post conditions to ensure that no mistakes are made + // Remember to track which proposals a user has voted yes for in the ballot resource + // and remember to include proper pre- and post-conditions to ensure that no mistakes are made // when a user submits their vote - access(all) resource Ballot { + access(all) resource ballot { } @@ -130,38 +103,29 @@ contract ApprovalVoting { ``` -Now is your chance to write some of your own Cadence code! -See if you can follow the instructions in the comments of the contract -to write your own approval voting contract. -Instructions for transactions are also included in the sample transactions. -Once you're done, share your project with the Flow community in the Flow discord! :) - -## Deploy the Contract - -:::info[Action] +Now is your chance to write some of your own Cadence code! See if you can follow the instructions in the comments of the contract to write your own approval voting contract. Instructions for transactions are also included in the sample transactions. Once you're done, share your project with the Flow community in the Flow Discord! -1. In the bottom right deployment modal, press the arrow to expand and make sure account `0x06` is selected as the signer. -2. Click the Deploy button to deploy it to account `0x06` +## Deploying the contract -::: +To deploy: -## Perform Voting +1. In the bottom right deployment modal, press the arrow to expand. +2. Make sure account `0x06` is selected as the signer. +3. Click the **Deploy** button to deploy it to account `0x06`. -Performing the common actions in this voting contract only takes three types of transactions. +## Perform voting -1. Initialize Proposals -2. Send `Ballot` to a voter -3. Cast Vote +Performing the common actions in this voting contract only takes three types of transactions: -We have a transaction for each step that we provide a skeleton of for you. -With the `ApprovalVoting` contract deployed to account `0x06`: +1. Initialize Proposals. +2. Send `Ballot` to a voter. +3. Cast Vote. -:::info[Action] +We have a transaction for each step, which provides a skeleton of for you. With the `ApprovalVoting` contract deployed to account `0x06`: -1. Open Transaction 1 which should have `Create Proposals` -2. Submit the transaction with account `0x06` selected as the only signer. +1. Open Transaction 1, which should have `Create Proposals` +2. Submit the transaction with account `0x06` selected as the only signer: -::: ```cadence CreateProposals.cdc import ApprovalVoting from 0x06 @@ -192,30 +156,19 @@ transaction { } ``` -This transaction allows the `Administrator` of the contract to create new proposals for voting and save them to the smart contract. They do this by calling the `initializeProposals` function on their stored `Administrator` resource, giving it two new proposals to vote on. -We use the `post` block to ensure that there were two proposals created, like we wished for. +This transaction allows the `Administrator` of the contract to create new proposals for voting and save them to the smart contract. They do this by calling the `initializeProposals` function on their stored `Administrator` resource, giving it two new proposals to vote on. We use the `post` block to ensure that there were two proposals created, as we intended. Next, the `Administrator` needs to hand out `Ballot`s to the voters. There isn't an easy `deposit` function this time for them to send a `Ballot` to another account, so how would they do it? -## Putting Resource Creation in public capabilities +## Putting resource creation in public capabilities -Unlike our other tutorial contracts, the Approval Voting contract -puts its Ballot creation function in a resource instead of as a public function in a contract. -This way, the admin can control who can and cannot create a Ballot resource. -There are also ways to consolidate all of the voting logic into the Admin resource -so that there can be multiple sets of proposals being voted on at the same time -without having to deploy a new contract for each one! +Unlike our other tutorial contracts, the Approval Voting contract puts its ballot creation function in a resource instead of as a public function in a contract. This way, the admin can control who can and cannot create a ballot resource. There are also ways to consolidate all of the voting logic into the Admin resource so that there can be multiple sets of proposals being voted on at the same time without having to deploy a new contract for each one! -Here, we're just exposing the create ballot function through a public capability -for simplicity, so lets use the transaction for a voter to create a ballot. - -:::info[Action] +Here, we're just exposing the create ballot function through a public capability for simplicity, so let's use the transaction for a voter to create a ballot: 1. Open the `Create Ballot` transaction. 2. Select account `0x07` as a signer. -3. Submit the transaction by clicking the `Send` button - -::: +3. Submit the transaction by clicking the `Send` button: ```cadence CreateBallot.cdc @@ -242,23 +195,15 @@ transaction { ``` -After this transaction, account `0x07` should now have a `Ballot` resource -object in its account storage. You can confirm this by selecting `0x07` -from the lower-left sidebar and seeing `Ballot` resource listed under the `Storage` field. +After this transaction, account `0x07` should now have a `Ballot` resource object in its account storage. You can confirm this by selecting `0x07` from the lower-left sidebar and seeing `Ballot` resource listed under the `Storage` field. -## Casting a Vote +## Casting a vote -Now that account `0x07` has a `Ballot` in their storage, they can cast their vote. -To do this, they will call the `vote` method on their stored resource, -then cast that `Ballot` by passing it to the `cast` function in the main smart contract. - -:::info[Action] +Now that account `0x07` has a `Ballot` in its storage, it can cast its vote. To do this, the account will call the `vote` method on its stored resource, then cast that `Ballot` by passing it to the `cast` function in the main smart contract: 1. Open the `Cast Ballot` transaction. 2. Select account `0x07` as the only transaction signer. -3. Click the `send` button to submit the transaction. - -::: +3. Click the `send` button to submit the transaction: ```cadence CastBallot.cdc import ApprovalVoting from 0x06 @@ -271,11 +216,11 @@ transaction { // fill in the correct entitlements! prepare(voter: auth() &Account) { - // Borrow a reference to the Ballot resource in the Voter's storage + // Borrow a reference to the ballot resource in the Voter's storage // Vote on the proposal - // Issue a capability to the Ballot resource in the voter's storage + // Issue a capability to the ballot resource in the voter's storage // Cast the vote by submitting it to the smart contract } @@ -286,19 +231,14 @@ transaction { } ``` -In this transaction, the user votes for one of the proposals by submitting -their votes on their own ballot and then sending the capability. +In this transaction, the user votes for one of the proposals by submitting its votes on its own ballot and then sending the capability. ## Reading the result of the vote -At any time, anyone could read the current tally of votes by directly reading the fields of the contract. You can use a script to do that, since it does not need to modify storage. - -:::info[Action] +At any time, anyone could read the current tally of votes by directly reading the fields of the contract. You can use a script to do that since it does not need to modify storage: 1. Open the `Get Votes` script. -2. Click the `execute` button to run the script. - -::: +2. Click the `execute` button to run the script: ```cadence GetVotes.cdc import ApprovalVoting from 0x06 @@ -319,11 +259,15 @@ access(all) fun main(): { } ``` -The return type should reflect the number of votes that were cast for each proposal -with the `Cast Vote` transaction. +The return type should reflect the number of votes that were cast for each proposal with the `Cast Vote` transaction. + +## Other voting possibilities + +This contract was a very simple example of voting in Cadence. It clearly couldn't be used for a real-world voting situation, but hopefully you can see what kind of features could be added to it to ensure practicality and security. -## Other Voting possibilities + -This contract was a very simple example of voting in Cadence. -It clearly couldn't be used for a real-world voting situation, -but hopefully you can see what kind of features could be added to it to ensure practicality and security. +[https://play.flow.com/e8e2af39-370d-4a52-9f0b-bfb3b12c7eff]: https://play.flow.com/e8e2af39-370d-4a52-9f0b-bfb3b12c7eff +[First Steps]: ./01-first-steps.md +[Hello World]: ./02-hello-world.md +[resource]: ../language/resources.mdx diff --git a/docs/tutorial/10-resources-compose.md b/docs/tutorial/10-resources-compose.md index 036e19a9..03416a6e 100644 --- a/docs/tutorial/10-resources-compose.md +++ b/docs/tutorial/10-resources-compose.md @@ -1,51 +1,33 @@ --- -title: 10. Composable Resources +title: Composable Resources --- In this tutorial, we're going to walk through how resources can own other resources by creating, deploying, and moving composable NFTs. ---- - -:::info[Action] - -This tutorial just includes example code. It does not have an associated playground project. -You are still welcome to copy this code and paste it to the playground to test it out though! +## Getting started -::: +This tutorial only includes example code. It does not have an associated playground project. You are still welcome to copy this code and paste it to the playground to test it out though! -Resources owning other resources is a powerful feature in the world of blockchain and smart contracts. +We'll cover how _Resources own other resources_, which is a powerful feature in the world of blockchain and smart contracts. -**Before proceeding with this tutorial**, we recommend following the instructions in [Getting Started](./01-first-steps.md), -[Hello, World!](./02-hello-world.md), -and [Resources](./03-resources.md) to learn about the Playground and Cadence. +**Before proceeding with this tutorial**, we recommend following the instructions in [Getting Started], [Hello World], and [Resources] to learn about the Playground and Cadence. -## Resources Owning Resources - ---- +## Resources owning resources -The NFT collections talked about in [Non-Fungible Tokens](./05-non-fungible-tokens-1.md) are examples of resources that own other resources. -We have a resource, the NFT collection, that has ownership of the NFT resources that are stored within it. -The owner and anyone with a reference can move these resources around, -but they still belong to the collection while they are in it and the code defined in the collection has ultimate control over the resources. +The NFT collections talked about in [Non-Fungible Tokens] are examples of resources that own other resources. We have a resource, the NFT collection, that has ownership of the NFT resources that are stored within it. The owner and anyone with a reference can move these resources around, but they still belong to the collection while they are in it, and the code defined in the collection has ultimate control over the resources. When the collection is moved or destroyed, all of the NFTs inside of it are moved or destroyed with it. -If the owner of the collection transferred the whole collection resource to another user's account, -all of the tokens will move to the other user's account with it. The tokens don't stay in the original owner's account. -This is like handing someone your wallet instead of just a dollar bill. It isn't a common action, but certainly is possible. +If the owner of the collection transferred the whole collection resource to another user's account, all of the tokens will move to the other user's account with it. The tokens don't stay in the original owner's account. This is like handing someone your wallet instead of just a dollar bill. It isn't a common action, but certainly it's possible. -References cannot be created for resources that are stored in other resources. -The owning resource has control over it and therefore controls the type of access that external calls have on the stored resource. +References cannot be created for resources that are stored in other resources. The owning resource has control over it and, therefore, controls the type of access that external calls have on the stored resource. -## Resources Owning Resources: An Example - ---- +### Example The NFT collection is a simple example of how resources can own other resources, but innovative and more powerful versions can be made. -An important feature of CryptoKitties (and other applications on the Ethereum blockchain) is that any developer can make new experiences around the existing application. -Even though the original contract didn't include specific support for CryptoKitty accessories (like hats), an independent developer was still able to make hats that Kitties from the original contract could use. +An important feature of CryptoKitties (and other applications on the Ethereum blockchain) is that any developer can make new experiences around the existing application. Even though the original contract didn't include specific support for CryptoKitty accessories (like hats), an independent developer was still able to make hats that Kitties from the original contract could use. Here is a basic example of how we can replicate this feature in Cadence: @@ -133,16 +115,16 @@ access(all) contract KittyVerse { These definitions show how a Kitty resource could own hats. -The hats are stored in a variable in the Kitty resource. +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} ``` -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. +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. -Here is a transaction to create a `Kitty` and a `KittyHat`, store the hat in the Kitty, then store it in your account storage. +Here is a transaction to create a `Kitty` and a `KittyHat`, store the hat in the Kitty, then store it in your account storage: ```cadence create_kitty.cdc import KittyVerse from 0x06 @@ -177,7 +159,7 @@ transaction { } ``` -Now we can run a transaction to move the Kitty along with its hat, remove the cowboy hat from the Kitty, then make the Kitty tip its hat. +Now, we can run a transaction to move the Kitty along with its hat, remove the cowboy hat from the Kitty, and then make the Kitty tip its hat: ```cadence tip_hat.cdc import KittyVerse from 0x06 @@ -211,24 +193,25 @@ transaction { If you were to run this transaction, you should see something like this output: -``` +```bash > "Howdy Y'all" > "Greetings, fellow aristocats!" ``` Whenever the Kitty is moved, its hats are implicitly moved along with it. This is because the hats are owned by the Kitty. -## The Future is Meow! Extensibility is coming! +## What's next ---- +The above is a simple example of composable resources. We had to explicitly say that a Kitty could own a Hat in this example, but Cadence now supports more powerful ways of achieving resource extensibility, where developers can declare types that separate resources can own, even if the owning resource never specified the ownership possibility in the first place. + +This feature is called [Attachments], and you should check out the documentation to learn about this powerful feature. -The above is a simple example of composable resources. -We had to explicitly say that a Kitty could own a Hat in this example, -but Cadence now supports more powerful ways of achieving resource extensibility -where developers can declare types that separate resources can own -even if the owning resource never specified the ownership possibility in the first place. +Practice what you've learned in the Flow Playground! -This feature is called [Attachments](https://cadence-lang.org/docs/language/attachments) -and you should check out the documentation to learn about this powerful feature! + -Practice what you're learned in the Flow Playground! +[Attachments]: ../language/attachments.mdx +[Getting Started]: ./01-first-steps.md +[Hello World]: ./02-hello-world.md +[Non-Fungible Tokens]: ./05-non-fungible-tokens-1.md +[Resources]: ./03-resources.md \ No newline at end of file