Selaa lähdekoodia

Add some more tutorial documentation (#60)

Armani Ferrante 4 vuotta sitten
vanhempi
sitoutus
d36ebd640a

+ 11 - 4
docs/src/.vuepress/config.js

@@ -42,19 +42,26 @@ module.exports = {
         title: "Getting Started",
         children: [
           "/getting-started/introduction",
-					"/getting-started/installation",
-					"/getting-started/quick-start",
+          "/getting-started/installation",
         ],
       },
       {
         collapsable: false,
-        title: "Tutorials",
-				children: [
+        title: "Programs on Solana",
+        children: [
           "/tutorials/tutorial-0",
           "/tutorials/tutorial-1",
           "/tutorials/tutorial-2",
           "/tutorials/tutorial-3",
           "/tutorials/tutorial-4",
+          "/tutorials/tutorial-5",
+        ],
+      },
+      {
+        collapsable: false,
+        title: "CLI",
+        children: [
+          "/cli/commands",
         ],
       },
     ],

+ 195 - 0
docs/src/cli/commands.md

@@ -0,0 +1,195 @@
+# Commands
+
+A CLI is provided to support building and managing an Anchor workspace.
+For a comprehensive list of commands and options, run `anchor -h` on any
+of the following subcommands.
+
+```
+anchor-cli
+
+USAGE:
+    anchor <SUBCOMMAND>
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
+SUBCOMMANDS:
+    build      Builds the workspace
+    deploy     Deploys each program in the workspace
+    idl        Commands for interacting with interface definitions
+    init       Initializes a workspace
+    launch     Deploys, initializes an IDL, and migrates all in one command
+    migrate    Runs the deploy migration script
+    new        Creates a new program
+    test       Runs integration tests against a localnetwork
+    upgrade    Upgrades a single program. The configured wallet must be the upgrade authority
+```
+
+## Init
+
+```
+anchor init
+```
+
+Initializes a project workspace wit the following structure.
+
+* `Anchor.toml`: Anchor configuration file.
+* `programs/`: Directory for Solana program crates.
+* `app/`: Directory for your application frontend.
+* `tests/`: Directory for JavaScript integration tests.
+* `migrations/deploy.js`: Deploy script.
+
+## Build
+
+```
+anchor build
+```
+
+Builds programs in the workspace targeting Solana's BPF runtime and emitting IDLs in the `target/idl` directory.
+
+## Deploy
+
+```
+anchor deploy
+```
+
+Deploys all programs in the workspace to the configured cluster.
+
+::: tip Note
+This is different from the `solana program deploy` command, because everytime it's run
+it will generate a *new* program address.
+:::
+
+## Upgrade
+
+```
+anchor upgrade <target/deplooy/program.so> --program-id <program-id>
+```
+
+Uses Solana's upgradeable BPF loader to upgrade the on chain program code.
+
+## Test
+
+```
+anchor test
+```
+
+Run an integration test suit against the configured cluster, deploying new versions
+of all workspace programs before running them.
+
+If the configured network is a localnet, then automatically starts the localnetwork and runs
+the test.
+
+::: tip Note
+The Anchor workflow [recommends](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/)
+to test your program using integration tests in a language other
+than Rust to make sure that bugs related to syntax misunderstandings
+are coverable with tests and not just replicated in tests.
+:::
+
+## Migrate
+
+```
+anchor migrate
+```
+
+Runs the deploy script located at `migrations/deploy.js`, injecting a provider configured
+form the workspace's `Anchor.toml`. For example,
+
+```javascript
+// File: migrations/deploys.js
+
+const anchor = require("@project-serum/anchor");
+
+module.exports = async function (provider) {
+  anchor.setProvider(provider);
+
+  // Add your deploy script here.
+}
+```
+
+Migrations are a new feature
+and only support this simple deploy script at the moment.
+
+## Idl
+
+The `idl` subcommand provides commands for interacting with interface definition files.
+It's recommended to use these commands to store an IDL on chain, at a deterministic
+address, as a function of nothing but the the program's ID. This
+allow us to generate clients for a program using nothing but the program ID.
+
+### Idl Init
+
+```
+anchor idl init -f <target/idl/program.json> <program-id>
+```
+
+Creates an idl account, writing the given `<target/idl/program.json>` file into a program owned account. By default, the size of the account is double the size of the IDL,
+allowing room for growth in case the idl needs to be upgraded in the future.
+
+### Idl Fetch
+
+```
+anchor idl fetch -o <out-file.json> <program-id>
+```
+
+Fetches an IDL from the configured blockchain. For example, make sure
+your `Anchor.toml` is pointing to the `mainnet` cluster and run
+
+```
+anchor idl fetch GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv
+```
+
+### Idl Authority
+
+```
+anchor idl authority <program-id>
+```
+
+Outputs the IDL account's authority. This is the wallet that has the ability to
+update the IDL.
+
+### Idl Erase Authority
+
+```
+anchor idl erase-authority -p <program-id>
+```
+
+Erases the IDL account's authority so that upgrades can no longer occur. The
+configured wallet must be the current authority.
+
+### Idl Upgrade
+
+```
+anchor idl upgrade <program-id> -f <target/idl/program.json>
+```
+
+Upgrades the IDL file on chain to the new `target/idl/program.json` idl.
+The configured wallet must be the current authority.
+
+```
+anchor idl set-authority -n <new-authority> -p <program-id>
+```
+
+Sets a new authority on the IDL account. Both the `new-authority` and `program-id`
+must be encoded in base 58.
+
+## Launch
+
+```
+anchor launch
+```
+
+Builds, deploys and migrates, all in one command. This is particularly
+useful when simultaneously developing an app against a Localnet or Devnet. For mainnet, it's
+recommended to run each command separately, since transactions can sometimes be
+unreliable depending on the Solana RPC node being used.
+
+## New
+
+```
+anchor new <program-name>
+```
+
+Creates a new program in the workspace's `programs/` directory initialized with boilerplate.

+ 2 - 2
docs/src/getting-started/installation.md

@@ -15,10 +15,10 @@ rustup component add rustfmt
 
 ## Install Solana
 
-See the solana [docs](https://docs.solana.com/cli/install-solana-cli-tools) for installation instructions. Version 1.5.0 is required. On macOS and Linux,
+See the solana [docs](https://docs.solana.com/cli/install-solana-cli-tools) for installation instructions. Version 1.5.5 is required. On macOS and Linux,
 
 ```bash
-sh -c "$(curl -sSfL https://release.solana.com/v1.5.0/install)"
+sh -c "$(curl -sSfL https://release.solana.com/v1.5.5/install)"
 ```
 
 ## Install Mocha

+ 1 - 1
docs/src/getting-started/introduction.md

@@ -2,7 +2,7 @@
 
 Anchor is a framework for Solana's [Sealevel](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192) runtime providing several convenient developer tools.
 
-- Rust crates and DSL for writing Solana programs
+- Rust crates and eDSL for writing Solana programs
 - [IDL](https://en.wikipedia.org/wiki/Interface_description_language) specification
 - TypeScript package for generating clients from IDL
 - CLI and workspace management for developing complete applications

+ 0 - 63
docs/src/getting-started/quick-start.md

@@ -1,63 +0,0 @@
-# Quick Start
-
-The quick start provides a whirlwind tour through creating, deploying, and testing a project
-using Anchor. For an in depth guide building the Anchor workflow and DSL from the ground up,
-see the subequent tutorials.
-
-## Initialize a project
-
-To initialize your project workspace, run
-
-```bash
-anchor init my-project && cd my-project
-```
-
-Your repo will be laid out with the following structure
-
-* `Anchor.toml`: Anchor configuration file.
-* `programs/`: Directory for Solana program crates.
-* `app/`: Directory for your application frontend.
-* `tests/`: Directory for JavaScript integration tests.
-
-## Build
-
-To build your program targeting Solana's BPF runtime and emit an IDL run
-
-```bash
-anchor build
-```
-
-You should see IDL files for all workspace programs in your `target/idl/` directory.
-
-## Deploy
-
-To deploy all programs in your workspace, run
-
-```
-anchor deploy
-```
-
-## Test
-
-It's [recommended](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/)
-to test your program using integration tests in a language other
-than Rust to make sure that bugs related to syntax misunderstandings
-are coverable with tests and not just replicated in tests.
-
-```
-anchor test
-```
-
-Testing will build a program, deploy it to a local network, and
-run integration tests all in one command.
-
-## New
-
-Anchor supports simulatenous development of multiple Solana programs.
-To create a new program in your workspace, run
-
-```
-anchor new <program-name>
-```
-
-You should see a new program in the `programs/` directory initialized with boilerplate.

+ 2 - 2
docs/src/tutorials/tutorial-0.md

@@ -1,4 +1,4 @@
-# Tutorial 0: A Minimal Example
+# A Minimal Example
 
 Here, we introduce Anchor's core syntax elements and project workflow. This tutorial assumes all
 [prerequisites](../getting-started/installation.md) are installed.
@@ -65,7 +65,7 @@ anchor build
 The `build` command is a convenience combining two steps.
 
 1) `cargo build-bpf`
-2) `anchor idl -f program/src/lib.rs -o target/idl/basic_0.json`.
+2) `anchor idl parse -f program/src/lib.rs -o target/idl/basic_0.json`.
 :::
 
 Once run, you should see your build artifacts, as usual, in your `target/` directory. Additionally,

+ 1 - 1
docs/src/tutorials/tutorial-1.md

@@ -1,4 +1,4 @@
-# Tutorial 1: Arguments and Accounts
+# Arguments and Accounts
 
 This tutorial covers the basics of creating and mutating accounts using Anchor.
 It's recommended to read [Tutorial 0](./tutorial-0.md) first, as this tutorial will

+ 42 - 13
docs/src/tutorials/tutorial-2.md

@@ -1,18 +1,22 @@
-# Tutorial 2: Account Constraints and Access Control
+# Account Constraints and Access Control
 
-This tutorial covers how to specify constraints and access control on accounts.
+This tutorial covers how to specify constraints and access control on accounts, a problem
+somewhat unique to the parallel nature of Solana.
 
-Because Solana programs are stateless, a transaction must specify accounts to be executed. And because an untrusted client specifies those accounts, a program must responsibily validate all input to the program to ensure it is what it claims to be--in addition to any instruction specific access control the program needs to do.
+On Solana, a transaction must specify all accounts required for execution. And because an untrusted client specifies those accounts, a program must responsibly validate all such accounts are what the client claims they are--in addition to any instruction specific access control the program needs to do.
 
-For example, one could imagine easily writing a faulty token program that forgets to check if the signer of a transaction claiming to be the owner of a token account actually matches the owner on the account. A simpler question that must be asked: what happens if the program expects a `Mint` account but a `Token` account is given instead?
+For example, one could imagine easily writing a faulty token program that forgets to check if the **signer** of a transaction claiming to be the **owner** of a Token `Account` actually matches the **owner** on that account. Furthermore, imagine what might happen if the program expects a `Mint` account but a malicious user gives a token `Account`.
 
+To address these problems, Anchor provides several types, traits, and macros. It's easiest to understand by seeing how they're used in an example, but a couple include
 
-Doing these checks is particularly burdensome when there are lots of dependencies between
-accounts, leading to repetitive [boilerplate](https://github.com/project-serum/serum-dex/blob/master/registry/src/access_control.rs)
-code for account validation along with the ability to easily shoot oneself in the foot.
-Instead, one can use the Anchor DSL to do these checks by specifying **constraints** when deriving
-`Accounts`. We briefly touched on the most basic (and important) type of account constraint in the
-[previous tutorial](./tutorial-1.md), the account discriminator. Here, we demonstrate others.
+* [Accounts](https://docs.rs/anchor-lang/0.1.0/anchor_lang/derive.Accounts.html): derive macro implementing the `Accounts` [trait](https://docs.rs/anchor-lang/0.1.0/anchor_lang/trait.Accounts.html), allowing a struct to transform
+from the untrusted `&[AccountInfo]` slice given to a Solana program into a validated struct
+of deserialized account types.
+* [#[account]](https://docs.rs/anchor-lang/0.1.0/anchor_lang/attr.account.html): attribute macro implementing [AccountSerialize](https://docs.rs/anchor-lang/0.1.0/anchor_lang/trait.AccountSerialize.html) and [AccountDeserialize](https://docs.rs/anchor-lang/0.1.0/anchor_lang/trait.AnchorDeserialize.html), automatically prepending a unique 8 byte discriminator to the account array. The discriminator is defined by the first 8 bytes of the `Sha256` hash of the account's Rust identifier--i.e., the struct type name--and ensures no account can be substituted for another.
+* [ProgramAccount](https://docs.rs/anchor-lang/0.1.0/anchor_lang/struct.ProgramAccount.html): a wrapper type for a deserialized account implementing `AccountDeserialize`. Using this type within an `Accounts` struct will ensure the account is **owned** by the currently executing program.
+
+With the above, we can define preconditions for our any instruction handler expecting a certain set of
+accounts, allowing us to more easily reason about the security of our programs.
 
 ## Clone the Repo
 
@@ -30,12 +34,37 @@ cd anchor/examples/tutorial/basic-2
 
 ## Defining a Program
 
-For now see the [source](https://github.com/project-serum/anchor/tree/master/examples/basic-2).
+Here we have a simple **Counter** program, where anyone can create a counter, but only the assigned
+**authority** can increment it.
+
+<<< @/../examples/tutorial/basic-2/programs/basic-2/src/lib.rs
+
+If you've gone through the previous tutorials the `create` instruction should be straightforward.
+Let's focus on the `increment` instruction, specifically the `Increment` struct deriving
+`Accounts`.
+
+```rust
+#[derive(Accounts)]
+pub struct Increment<'info> {
+    #[account(mut, has_one = authority)]
+    pub counter: ProgramAccount<'info, Counter>,
+    #[account(signer)]
+    pub authority: AccountInfo<'info>,
+}
+```
+
+Here, several `#[account(..)]` attributes are used.
+
+* `mut`: tells the program to persist all changes to the account.
+* `has_one`: enforces the constraint that `Increment.counter.authority == Increment.authority.key`.
+* `signer`: enforces the constraint that the `authority` account **signed** the transaction.
 
-TODO
+If any of these constraints do not hold, then the `increment` instruction will never be executed.
+This allows us to completely separate account validation from our program's business logic, allowing us
+to reason about each concern more easily. For more, see the full [list](https://github.com/project-serum/anchor#accounts-attribute-syntax) of account constraints.
 
 ## Next Steps
 
 We've covered the basics for writing a single program using Anchor on Solana. But the power of
 blockchains come not from a single program, but from combining multiple *composable* programs
-(buzzword alert!). Next, we'll see how to call one program from another.
+(buzzword...check). Next, we'll see how to call one program from another.

+ 1 - 1
docs/src/tutorials/tutorial-3.md

@@ -1,4 +1,4 @@
-# Tutorial 3: Cross Program Invocations (CPI)
+# Cross Program Invocations (CPI)
 
 This tutorial covers how to call one program from another, a process known as
 *cross-program-invocation* (CPI).

+ 47 - 4
docs/src/tutorials/tutorial-4.md

@@ -1,4 +1,4 @@
-# Tutorial 4: State structs
+# State structs
 
 Up until now, we've treated programs on Solana as stateless, using accounts to persist
 state between instruction invocations. In this tutorial, we'll give Solana programs the
@@ -23,10 +23,53 @@ cd anchor/examples/tutorial/basic-4
 
 <<< @/../examples/tutorial/basic-4/programs/basic-4/src/lib.rs#code
 
-TODO: explain + add instructions (both manual instructions and instructions inside the impl block).
+Unlike the previous examples, all the instructions here not only take in an `Accounts`
+struct, but they also operate over a mutable, global account marked by the `#[state]`
+attribute. Every instruction defined in the corresponding `impl` block will have access
+to this account, making it a great place to store global program state.
+
+### How it works
+
+We are able to give a program the illusion of state by adopting conventions in the framework.  When invoking the `new` constructor, Anchor will automatically create a
+program-owned account inside the program itself, invoking the system program's [create_account_with_seed](https://docs.rs/solana-program/1.5.5/solana_program/system_instruction/fn.create_account_with_seed.html) instruction, using `Pubkey::find_program_address(&[], program_id)` as the **base** and a deterministic string as the **seed** (the string doesn't
+matter, as long as the framework is consistent).
+
+This all has the effect of
+giving the `#[state]` account a deterministic address, and so as long as all clients
+and programs adopt this convention, programs can have the illusion of state in addition
+to the full power of the lower level Solana accounts API. Of course, Anchor will handle this all for you, so you never have to worry about these details.
 
 ## Using the client
 
-<<< @/../examples/tutorial/basic-4/tests/basic-4.js#code
+### Invoke the constructor
+
+To access the `#[state]` account and associated instructions, one can use the
+`anchor.state` namespace on the client. For example, to invoke the constructor,
+
+<<< @/../examples/tutorial/basic-4/tests/basic-4.js#ctor
+
+Note that the constructor can only be invoked once per program. All subsequent calls
+to it will fail, since, as explained above, an account at a deterministic address
+will be created.
+
+### Fetch the state
+
+To fetch the state account,
+
+<<< @/../examples/tutorial/basic-4/tests/basic-4.js#accessor
+
+### Invoke an instruction
+
+To invoke an instruction,
+
+<<< @/../examples/tutorial/basic-4/tests/basic-4.js#instruction
+
+## Conclusion
+
+Using state structs is intuitive. However, due to the fact that accounts
+on Solana have a fixed size, applications often need to use accounts
+directly in addition to `#[state]` stucts.
+
+## Next Steps
 
-TODO explain.
+Next we'll discuss errors.

+ 61 - 0
docs/src/tutorials/tutorial-5.md

@@ -0,0 +1,61 @@
+# Errors
+
+If you've ever programmed on a blockchain, you've probably been frustrated by
+either non existant or opaque error codes. Anchor attempts to address this by
+providing the `#[error]` attribute, which can be used to create typed Errors with
+descriptive messages that automatically propagate to the client.
+
+## Defining a Program
+
+For example,
+
+```rust
+use anchor_lang::prelude::*;
+
+#[program]
+mod errors {
+    use super::*;
+    pub fn hello(_ctx: Context<Hello>) -> Result<()> {
+        Err(ErrorCode::Hello.into())
+    }
+}
+
+#[derive(Accounts)]
+pub struct Hello {}
+
+#[error]
+pub enum ErrorCode {
+    #[msg("This is an error message clients will automatically display")]
+    Hello,
+}
+```
+
+Observe the [#[error]](https://docs.rs/anchor-lang/0.1.0/anchor_lang/attr.error.html) attribute on the `ErrorCode` enum. This macro generates two types: an `Error` and a `Result`, both of which can be used when returning from your program.
+
+To use the `Error`, one can simply use the user defined `ErrorCode` with Rust's [From](https://doc.rust-lang.org/std/convert/trait.From.html) trait. If you're unfamiliar with `From`, no worries. Just know that you need to either call
+`.into()` when using your `ErrorCode`. Or use Rust's `?` operator, when returning an error.
+Both of these will automatically convert *into* the correct `Error`.
+
+::: details
+What's the deal with this From stuff? Well, because the Solana runtime expects a [ProgramError](https://docs.rs/solana-program/1.5.5/solana_program/program_error/enum.ProgramError.html) in the return value. The framework needs to wrap the user defined error code into a
+`ProgramError::Code` variant, before returning. The alternative would be to use the
+`ProgramError` directly.
+:::
+
+## Using the Client
+
+When using the client, we get the error message.
+
+```javascript
+try {
+  const tx = await program.rpc.hello();
+  assert.ok(false);
+} catch (err) {
+  const errMsg = "This is an error message clients will automatically display";
+  assert.equal(err.toString(), errMsg);
+}
+```
+
+It's that easy. :)
+
+To run the full example, go [here](https://github.com/project-serum/anchor/tree/master/examples/errors).

+ 6 - 9
examples/errors/programs/errors/src/lib.rs

@@ -1,3 +1,6 @@
+//! This example demonstrates how custom errors and associated error messsages
+//! can be defined and transparently propagated to clients.
+
 #![feature(proc_macro_hygiene)]
 
 use anchor_lang::prelude::*;
@@ -5,15 +8,15 @@ use anchor_lang::prelude::*;
 #[program]
 mod errors {
     use super::*;
-    pub fn hello(ctx: Context<Hello>) -> Result<()> {
+    pub fn hello(_ctx: Context<Hello>) -> Result<()> {
         Err(MyError::Hello.into())
     }
 
-    pub fn hello_no_msg(ctx: Context<HelloNoMsg>) -> Result<()> {
+    pub fn hello_no_msg(_ctx: Context<Hello>) -> Result<()> {
         Err(MyError::HelloNoMsg.into())
     }
 
-    pub fn hello_next(ctx: Context<HelloNext>) -> Result<()> {
+    pub fn hello_next(_ctx: Context<Hello>) -> Result<()> {
         Err(MyError::HelloNext.into())
     }
 }
@@ -21,12 +24,6 @@ mod errors {
 #[derive(Accounts)]
 pub struct Hello {}
 
-#[derive(Accounts)]
-pub struct HelloNoMsg {}
-
-#[derive(Accounts)]
-pub struct HelloNext {}
-
 #[error]
 pub enum MyError {
     #[msg("This is an error message clients will automatically display")]

+ 14 - 77
examples/tutorial/basic-2/programs/basic-2/src/lib.rs

@@ -8,43 +8,16 @@ use anchor_lang::prelude::*;
 mod basic_2 {
     use super::*;
 
-    pub fn create_author(
-        ctx: Context<CreateAuthor>,
-        authority: Pubkey,
-        name: String,
-    ) -> ProgramResult {
-        let author = &mut ctx.accounts.author;
-        author.authority = authority;
-        author.name = name;
+    pub fn create(ctx: Context<Create>, authority: Pubkey) -> ProgramResult {
+        let counter = &mut ctx.accounts.counter;
+        counter.authority = authority;
+        counter.count = 0;
         Ok(())
     }
 
-    pub fn update_author(ctx: Context<UpdateAuthor>, name: String) -> ProgramResult {
-        let author = &mut ctx.accounts.author;
-        author.name = name;
-        Ok(())
-    }
-
-    pub fn create_book(ctx: Context<CreateBook>, title: String, pages: Vec<Page>) -> ProgramResult {
-        let book = &mut ctx.accounts.book;
-        book.author = *ctx.accounts.author.to_account_info().key;
-        book.title = title;
-        book.pages = pages;
-        Ok(())
-    }
-
-    pub fn update_book(
-        ctx: Context<UpdateBook>,
-        title: Option<String>,
-        pages: Option<Vec<Page>>,
-    ) -> ProgramResult {
-        let book = &mut ctx.accounts.book;
-        if let Some(title) = title {
-            book.title = title;
-        }
-        if let Some(pages) = pages {
-            book.pages = pages;
-        }
+    pub fn increment(ctx: Context<Increment>) -> ProgramResult {
+        let counter = &mut ctx.accounts.counter;
+        counter.count += 1;
         Ok(())
     }
 }
@@ -52,60 +25,24 @@ mod basic_2 {
 // Define the validated accounts for each handler.
 
 #[derive(Accounts)]
-pub struct CreateAuthor<'info> {
-    #[account(init)]
-    pub author: ProgramAccount<'info, Author>,
-    pub rent: Sysvar<'info, Rent>,
-}
-
-#[derive(Accounts)]
-pub struct UpdateAuthor<'info> {
-    #[account(signer)]
-    pub authority: AccountInfo<'info>,
-    #[account(mut, "&author.authority == authority.key")]
-    pub author: ProgramAccount<'info, Author>,
-}
-
-#[derive(Accounts)]
-pub struct CreateBook<'info> {
-    #[account(signer)]
-    pub authority: AccountInfo<'info>,
-    #[account("&author.authority == authority.key")]
-    pub author: ProgramAccount<'info, Author>,
+pub struct Create<'info> {
     #[account(init)]
-    pub book: ProgramAccount<'info, Book>,
+    pub counter: ProgramAccount<'info, Counter>,
     pub rent: Sysvar<'info, Rent>,
 }
 
 #[derive(Accounts)]
-pub struct UpdateBook<'info> {
+pub struct Increment<'info> {
+    #[account(mut, has_one = authority)]
+    pub counter: ProgramAccount<'info, Counter>,
     #[account(signer)]
     pub authority: AccountInfo<'info>,
-    #[account("&author.authority == authority.key")]
-    pub author: ProgramAccount<'info, Author>,
-    #[account(mut, belongs_to = author)]
-    pub book: ProgramAccount<'info, Book>,
 }
 
 // Define the program owned accounts.
 
 #[account]
-pub struct Author {
+pub struct Counter {
     pub authority: Pubkey,
-    pub name: String,
-}
-
-#[account]
-pub struct Book {
-    pub author: Pubkey,
-    pub title: String,
-    pub pages: Vec<Page>,
-}
-
-// Define custom types.
-
-#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
-pub struct Page {
-    pub content: String,
-    pub footnote: String,
+    pub count: u64,
 }

+ 18 - 84
examples/tutorial/basic-2/tests/basic-2.js

@@ -7,107 +7,41 @@ describe("basic-2", () => {
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);
 
-  // Author for the tests.
-  const author = new anchor.web3.Account();
+  // Counter for the tests.
+  const counter = new anchor.web3.Account();
 
   // Program for the tests.
   const program = anchor.workspace.Basic2;
 
-  it("Creates an author", async () => {
-    await program.rpc.createAuthor(provider.wallet.publicKey, "Ghost", {
+  it("Creates a counter", async () => {
+    await program.rpc.create(provider.wallet.publicKey, {
       accounts: {
-        author: author.publicKey,
+        counter: counter.publicKey,
         rent: anchor.web3.SYSVAR_RENT_PUBKEY,
       },
-      signers: [author],
-      instructions: [
-        anchor.web3.SystemProgram.createAccount({
-          fromPubkey: provider.wallet.publicKey,
-          newAccountPubkey: author.publicKey,
-          space: 8 + 1000,
-          lamports: await provider.connection.getMinimumBalanceForRentExemption(
-            8 + 1000
-          ),
-          programId: program.programId,
-        }),
-      ],
+      signers: [counter],
+        instructions: [
+            await program.account.counter.createInstruction(counter),
+        ],
     });
 
-    let authorAccount = await program.account.author(author.publicKey);
+    let counterAccount = await program.account.counter(counter.publicKey);
 
-    assert.ok(authorAccount.authority.equals(provider.wallet.publicKey));
-    assert.ok(authorAccount.name === "Ghost");
+    assert.ok(counterAccount.authority.equals(provider.wallet.publicKey));
+    assert.ok(counterAccount.count.toNumber() === 0);
   });
 
-  it("Updates an author", async () => {
-    await program.rpc.updateAuthor("Updated author", {
+  it("Updates a counter", async () => {
+    await program.rpc.increment({
       accounts: {
-        author: author.publicKey,
+        counter: counter.publicKey,
         authority: provider.wallet.publicKey,
       },
     });
 
-    authorAccount = await program.account.author(author.publicKey);
+    counterAccount = await program.account.counter(counter.publicKey);
 
-    assert.ok(authorAccount.authority.equals(provider.wallet.publicKey));
-    assert.ok(authorAccount.name === "Updated author");
-  });
-
-  // Book params to use accross tests.
-  const book = new anchor.web3.Account();
-  const pages = [
-    {
-      content: "first page",
-      footnote: "first footnote",
-    },
-    {
-      content: "second page",
-      footnote: "second footnote",
-    },
-  ];
-
-  it("Creates a book", async () => {
-    await program.rpc.createBook("Book title", pages, {
-      accounts: {
-        authority: provider.wallet.publicKey,
-        author: author.publicKey,
-        book: book.publicKey,
-        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
-      },
-      signers: [book],
-      instructions: [
-        anchor.web3.SystemProgram.createAccount({
-          fromPubkey: provider.wallet.publicKey,
-          newAccountPubkey: book.publicKey,
-          space: 8 + 1000,
-          lamports: await provider.connection.getMinimumBalanceForRentExemption(
-            8 + 1000
-          ),
-          programId: program.programId,
-        }),
-      ],
-    });
-
-    const bookAccount = await program.account.book(book.publicKey);
-
-    assert.ok(bookAccount.author.equals(author.publicKey));
-    assert.ok(bookAccount.title === "Book title");
-    assert.deepEqual(bookAccount.pages, pages);
-  });
-
-  it("Updates a book", async () => {
-    await program.rpc.updateBook("New book title", null, {
-      accounts: {
-        authority: provider.wallet.publicKey,
-        author: author.publicKey,
-        book: book.publicKey,
-      },
-    });
-
-    const bookAccount = await program.account.book(book.publicKey);
-
-    assert.ok(bookAccount.author.equals(author.publicKey));
-    assert.ok(bookAccount.title === "New book title");
-    assert.deepEqual(bookAccount.pages, pages);
+    assert.ok(counterAccount.authority.equals(provider.wallet.publicKey));
+    assert.ok(counterAccount.count.toNumber() == 1);
   });
 });

+ 7 - 2
examples/tutorial/basic-4/tests/basic-4.js

@@ -10,26 +10,31 @@ describe("basic-4", () => {
   const program = anchor.workspace.Basic4;
 
   it("Is runs the constructor", async () => {
-    // #region code
+    // #region ctor
     // Initialize the program's state struct.
     await program.state.rpc.new({
       accounts: {
         authority: provider.wallet.publicKey,
       },
     });
+    // #endregion ctor
 
     // Fetch the state struct from the network.
+    // #region accessor
     const state = await program.state();
-    // #endregion code
+    // #endregion accessor
+
     assert.ok(state.count.eq(new anchor.BN(0)));
   });
 
   it("Executes a method on the program", async () => {
+    // #region instruction
     await program.state.rpc.increment({
       accounts: {
         authority: provider.wallet.publicKey,
       },
     });
+    // #endregion instruction
     const state = await program.state();
     assert.ok(state.count.eq(new anchor.BN(1)));
   });