# Source: https://docs.near.org/tutorials/examples/guest-book.md

---
id: guest-book
title: Guest Book
description: "Create a NEAR Guest Book smart contract to store user messages, attach NEAR tokens, and integrate with a frontend, including premium messages and testing instructions"
---





This example demonstrates how to create a simple guest book application on the NEAR blockchain, allowing users to sign messages and optionally attach a small amount of NEAR as a tip. It includes both the smart contract and the frontend components.

Our Guest Book example is a simple app composed by two main components:

1. A smart contract that stores messages from users, allowing to attach money to them.
2. A simple web-based frontend that displays the last 10 messages posted.

![img](/assets/docs/tutorials/examples/guest-book.png)

---

## Obtaining the Guest book Example

You have two options to start the Guest book Example.

1. You can use the app through `GitHub Codespaces`, which will open a web-based interactive environment.
2. Clone the repository locally and use it from your computer.

| Codespaces                                                                                                                        | Clone locally                                             |
|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/near-examples/guest-book-examples) | 🌐 `https://github.com/near-examples/guest-book-examples` |

---

## Structure of the Example

The example is divided in two main components:

1. The smart contract, available in two flavors: Rust and JavaScript
2. The frontend, that interacts with an already deployed contract.

<Tabs groupId="code-tabs">

  <TabItem value="js" label="🌐 JavaScript">

```bash
┌── sandbox-ts # sandbox testing
│    ├── src
│    │    └── main.ava.ts
│    ├── ava.config.cjs
│    └── package.json
├── src # contract's code
│    ├── contract.ts
│    └── model.ts
├── package.json # package manager
├── README.md
└── tsconfig.json # test script
```

  </TabItem>

  <TabItem value="rust" label="🦀 Rust">

```bash
┌── tests # workspaces testing
│    ├── workspaces.rs
├── src # contract's code
│    └── lib.rs
├── Cargo.toml # package manager
├── README.md
└── rust-toolchain.toml
```

  </TabItem>

</Tabs>

---

## Frontend

The guest book example includes a frontend that interacts with an already deployed smart contract, allowing user to sign a message.

<hr class="subsection" />

### Running the Frontend

To start the frontend you will need to install the dependencies and start the server.

```bash
cd frontend
yarn
yarn dev
```

Go ahead and login with your NEAR account. If you don't have one, you will be able to create one in the moment. Once logged in, you will be able to sign a message in the guest book. You can further send some money alongside your message. If you attach more than 0.01Ⓝ then your message will be marked as "premium".

<hr class="subsection" />

### Understanding the Frontend

The frontend is a [Next.JS](https://nextjs.org/) project generated by [create-near-app](https://github.com/near/create-near-app). Check `_app.js` and `index.js` to understand how components are displayed and interacting with the contract.

<Language value="js" language="js">
  ```
import '@/styles/globals.css';

import '@near-wallet-selector/modal-ui/styles.css';
import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet';
import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet';
import { setupMeteorWalletApp } from '@near-wallet-selector/meteor-wallet-app';
import { setupBitteWallet } from '@near-wallet-selector/bitte-wallet';
import { setupEthereumWallets } from '@near-wallet-selector/ethereum-wallets';
import { setupHotWallet } from '@near-wallet-selector/hot-wallet';
import { setupLedger } from '@near-wallet-selector/ledger';
import { setupSender } from '@near-wallet-selector/sender';
import { setupHereWallet } from '@near-wallet-selector/here-wallet';
import { setupNearMobileWallet } from '@near-wallet-selector/near-mobile-wallet';
import { setupWelldoneWallet } from '@near-wallet-selector/welldone-wallet';
import { GuestbookNearContract, NetworkId } from '@/config';
import { WalletSelectorProvider } from '@near-wallet-selector/react-hook';
import { Navigation } from '@/components/Navigation';
import { wagmiAdapter, web3Modal } from '@/wallets/web3modal';

 
const walletSelectorConfig = {
  network: NetworkId,
  createAccessKeyFor: GuestbookNearContract,
  modules: [
    setupEthereumWallets({ wagmiConfig: wagmiAdapter.wagmiConfig, web3Modal }),
    setupBitteWallet(),
    setupMeteorWallet(),
    setupMeteorWalletApp({contractId: GuestbookNearContract}),
    setupHotWallet(),
    setupLedger(),
    setupSender(),
    setupHereWallet(),
    setupNearMobileWallet(),
    setupWelldoneWallet(),
    setupMyNearWallet(),
  ],
}

export default function App({ Component, pageProps }) {

  return (
    <WalletSelectorProvider config={walletSelectorConfig}>
      <Navigation />
      <Component {...pageProps} />
    </WalletSelectorProvider>
  );
}
```
  ```
import { utils } from "near-api-js";
import { useState, useEffect } from "react";

import Form from "@/components/Form";
import SignIn from "@/components/SignIn";
import Messages from "@/components/Messages";
import styles from "@/styles/app.module.css";

import { GuestbookNearContract } from "@/config";
import { useWalletSelector } from '@near-wallet-selector/react-hook';


export default function Home() {
  const { signedAccountId, viewFunction, callFunction } = useWalletSelector();
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    getLast10Messages().then((messages) => setMessages(messages.reverse()));
  }, []);

  const getLast10Messages = async () => {
    const total_messages = await viewFunction({
      contractId: GuestbookNearContract,
      method: "total_messages",
    });
    const from_index = total_messages >= 10 ? total_messages - 10 : 0;
    return viewFunction({
      contractId: GuestbookNearContract,
      method: "get_messages",
      args: { from_index: String(from_index), limit: "10" },
    });
  };

  const onSubmit = async (e) => {
    e.preventDefault();

    const { fieldset, message, donation } = e.target.elements;

    fieldset.disabled = true;

    // Add message to the guest book
    const deposit = utils.format.parseNearAmount(donation.value);
    callFunction({
      contractId: GuestbookNearContract,
      method: "add_message",
      args: { text: message.value },
      deposit,
    }).catch(() => {
      // rollback to the current messages
      setMessages(messages);
    });

    await new Promise(resolve => setTimeout(resolve, 300));
    fieldset.disabled = false;
    setMessages([{ sender: signedAccountId, text: message.value, premium: donation.value >= 1 }, ...messages]);
  };

  return (
    <main className={styles.main}>
      <div className="container">
        <h1>📖 NEAR Guest Book</h1>
        {signedAccountId ? (
          <Form onSubmit={onSubmit} currentAccountId={signedAccountId} />
        ) : (
          <SignIn />
        )}
      </div>

      {!!messages.length && <Messages messages={messages} />}
    </main>
  );
}

```
</Language>

---

## Smart Contract

The contract presents 3 methods: `add_message`, `get_message` and `total_messages`.

<CodeTabs>
  <Language value="js" language="ts">
    ```
@NearBindgen({})
class GuestBook {
  messages: Vector<PostedMessage> = new Vector<PostedMessage>("v-uid");

  static schema = {
    'messages': { class: Vector, value: PostedMessage }
  }

  @call({ payableFunction: true })
  // Public - Adds a new message.
  add_message({ text }: { text: string }) {
    // If the user attaches more than 0.1N the message is premium
    const premium = near.attachedDeposit() >= BigInt(POINT_ONE);
    const sender = near.predecessorAccountId();

    const message = new PostedMessage(premium, sender, text);
    this.messages.push(message);
  }

  @view({})
  // Returns an array of messages.
  get_messages({ from_index = 0, limit = 10 }: { from_index: number, limit: number }): PostedMessage[] {
    return this.messages.toArray().slice(from_index, from_index + limit);
  }

```
  </Language>
  <Language value="rust" language="rust">
    ```
#[near]
impl Contract {
    // Public Method - Adds a message to the vector
    #[payable]
    pub fn add_message(&mut self, text: String) {
        // If the user attaches more than 0.01N the message is premium
        let premium = env::attached_deposit() >= POINT_ONE;
        let sender = env::predecessor_account_id();

        let message = PostedMessage {
            premium,
            sender,
            text,
        };

        self.messages.push(message);
    }

    // Public Method - Returns a slice of the messages.
    pub fn get_messages(&self, from_index: Option<U64>, limit: Option<U64>) -> Vec<&PostedMessage> {
        let from = u64::from(from_index.unwrap_or(U64(0)));
        let limit = u64::from(limit.unwrap_or(U64(10)));

        self.messages
            .iter()
            .skip(from as usize)
            .take(limit as usize)
            .collect()
    }

    pub fn total_messages(&self) -> u32 {
        self.messages.len()
    }
}

```
  </Language>
  
</CodeTabs>

<hr class="subsection" />

### Testing the Contract

The contract readily includes a set of unit and sandbox testing to validate its functionality. To execute the tests, run the following commands:

<Tabs groupId="code-tabs">
  <TabItem value="js" label="🌐 JavaScript">

```bash
cd contract-ts
yarn
yarn test
```

  </TabItem>
  <TabItem value="rust" label="🦀 Rust">
  
  ```bash
  cd contract-rs
  cargo test
  ```

  </TabItem>

</Tabs>

:::tip
The `integration tests` use a sandbox to create NEAR users and simulate interactions with the contract.
:::

<hr class="subsection" />

### Deploying the Contract to the NEAR network

In order to deploy the contract you will need to create a NEAR account.

<Tabs groupId="cli-tabs">
  <TabItem value="short" label="Short">

  ```bash
  # Create a new account pre-funded by a faucet
  near create-account <accountId> --useFaucet
  ```
  </TabItem>

  <TabItem value="full" label="Full">

  ```bash
  # Create a new account pre-funded by a faucet
  near account create-account sponsor-by-faucet-service <my-new-dev-account>.testnet autogenerate-new-keypair save-to-keychain network-config testnet create
  ```
  </TabItem>
</Tabs>

Go into the directory containing the smart contract (`cd contract-ts` or `cd contract-rs`), build and deploy it:

<Tabs groupId="code-tabs">

  <TabItem value="js" label="🌐 JavaScript">

    ```bash
    npm run build
    near deploy <accountId> ./build/guest_book.wasm
    ```

  </TabItem>
  <TabItem value="rust" label="🦀 Rust">
  
  ```bash
  cargo near deploy build-non-reproducible-wasm <accountId>
  ```

  </TabItem>

</Tabs>

:::tip
To interact with your contract from the [frontend](#frontend), simply replace the variable `CONTRACT_NAME` in the `index.js` file.
:::

<hr class="subsection" />

### CLI: Interacting with the Contract

To interact with the contract through the console, you can use the following commands.

#### Get messages

<Tabs groupId="cli-tabs">
  <TabItem value="short" label="Short">

  ```bash
  near view guestbook.near-examples.testnet get_messages '{"from_index": "0","limit": "10"}'
  ```
  </TabItem>

  <TabItem value="full" label="Full">

  ```bash
  near contract call-function as-read-only guestbook.near-examples.testnet get_messages json-args '{"from_index": "0","limit": "10"}' network-config testnet now
  ```
  </TabItem>
</Tabs>

<hr class="subsection" />

#### Get total number of messages

<Tabs groupId="cli-tabs">
  <TabItem value="short" label="Short">

  ```bash
  near view guestbook.near-examples.testnet total_messages
  ```
  </TabItem>

  <TabItem value="full" label="Full">

  ```bash
  near contract call-function as-read-only guestbook.near-examples.testnet total_messages json-args {} network-config testnet now
  ```
  </TabItem>
</Tabs>

<hr class="subsection" />

#### Add a message

<Tabs groupId="cli-tabs">
  <TabItem value="short" label="Short">

  ```bash
  # Replace <accountId> with your account ID
  # Required a text
  # Optional deposit to make the message premium
  near call guestbook.near-examples.testnet add_message '{"text":"Hello Near"}' --accountId <accountId> --deposit 0.1
  ```
  </TabItem>

  <TabItem value="full" label="Full">

  ```bash
  # Replace <accountId> with your account ID
  # Required a text
  # Optional deposit to make the message premium
  near contract call-function as-transaction guestbook.near-examples.testnet add_message json-args '{"text":"Hello Near"}' prepaid-gas '30.0 Tgas' attached-deposit '0.1 NEAR' sign-as <accountId> network-config testnet sign-with-keychain send
  ```
  </TabItem>
</Tabs>

<hr class="subsection" />

:::tip
If you're using your own account, replace `guestbook.near-examples.testnet` with your `accountId`.
:::

---

## Moving Forward

A nice way to learn is by trying to expand a contract. You can modify the guestbook example to incorporate a feature where users can give likes to messages. Additionally, implement a method to toggle the like.

<MovingForwardSupportSection />

:::note Versioning for this article

At the time of this writing, this example works with the following versions:

- near-cli: `4.0.13`
- node: `18.19.1`
- rustc: `1.77.0`

:::