# Build a Proof of Existence dApp (ink!)

![Ink! 5.0](https://2951078474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFWgzFJTgbmbm8iaxYNRS%2Fuploads%2Fgit-blob-845a4bde55d13126d29016ee74855161b026fb81%2Fink-5.0.svg?alt=media)

## Objective

In this tutorial, we will walk through a full dApp development experience and write out both the [ink! smart contract](https://use.ink/) and the [React front end](https://react.dev/). We will also deploy the smart contract on a CESS local node and have the front end interact with it.

The application that we will develop is a Proof of Existence (PoE) dApp. It is used to prove that 1) one is the owner of a particular file, and 2) the file has the same content as it is at the point it was published on-chain. To understand the use case of the second point, think about a user who want to prove the content of a business contract, written in the form of a smart contract, has not been tampered with from the point it was published.

Instead of posting the whole file content on-chain, we will extract the first 64kB of the content, pass it to a hash function, and claim ownership of the file. Users can also send transactions on-chain to retrieve a list of their owned files and check if a file has been claimed. Note that this hashing mechanism is not secure for production use and serves only as demonstration purpose here.

{% hint style="success" %}
The complete source code of this tutorial can be seen at [the github repository](https://github.com/CESSProject/cess-examples/tree/main).

* For the smart contract, refer to [`ink/poe` directory](https://github.com/CESSProject/cess-examples/tree/main/ink/poe).
* For front end, refer to [the `src/ProofOfExistenceInk.js`](https://github.com/CESSProject/cess-examples/blob/main/frontend/src/ProofOfExistenceInk.js).
  {% endhint %}

We will first code the ink! smart contract side and then go back to the front-end side. Let's jump right in!

## ink! Smart Contract

### Prerequisites

This section has the same prerequisites as the tutorial [Deploy an ink! Smart Contract](https://doc.cess.network/developer/deploy-sc-ink#prerequisite). Please follow that section and install all required components: Rust and `cargo-contract`.

### Development

1. Let's start by building the directory structure

   ```bash
   mkdir poe-ink
   cd poe-ink
   cargo contract new contract
   ```

   The last command will create an ink! contract project skeleton.

   Inside the directory, there are three files.

   ```
   poe-ink/contract/
     ∟ .gitignore    # contains files to ignore when committing to git
     ∟ Cargo.toml    # This is a Rust project, so there is a `Cargo.toml` file for the project specification.
     ∟ lib.rs        # The actual smart contract and unit test code.
   ```

   The most interesting file is the `lib.rs`. It is a simple contract that reads and flips a boolean value. Please skim through it to get an idea of how ink! contract code is structured.
2. Let's build and test the contract project.

   ```bash
   cd contract          # In case you haven't gotten inside the contract dir.
   cargo contract build # This command builds the contract project.
   cargo test           # This command runs the unit test code starting at the `mod tests` line in the code.
   ```

   Running the `cargo contract build` yields three files:

   * `contract.wasm`: the contract code
   * `contract.json`: the contract metadata
   * `contract.contract`: the contract code and metadata

   The front end (see next section) will need to read `contract.json` to know the API of the contract. We will use `contract.contract` to instantiate the contract on-chain.
3. Open `Cargo.toml` and add dependencies as follows

   ```toml
     [dependencies]
     ink = { version = "5.0.0", default-features = false }

     scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
     scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }
     
     ...

     std = [
       "ink/std",
       "scale/std",
       "scale-info/std",
     ]

   ```
4. Open `lib.rs`. Let's remove everything and only keep the top-level structure. So we have:

   ```rs
   #![cfg_attr(not(feature = "std"), no_std, no_main)]

   #[ink::contract]
   mod contract {
     // We will fill up the code here next
   }
   ```
5. In smart contract development, two of the keys are deciding how the storage data structure looks like and what events it emits. Let's think about the PoE functionality. We will need a storage from user addresses mapping to an array of files, indicating the hash digests of the files they owned; and a reverse map from the hash digest to its owner. Currently we don't support the same file / file digest to be owned by multiple users. The following storage structure supports this:

   ```rs
   mod contract {
     // The following two lines are added to import the support of vector `Vec` and `Mapping` data structure.
     use ink::prelude::{vec, vec::Vec};
     use ink::storage::Mapping;

     #[ink(storage)]
     pub struct Contract {
       /// Mapping from AccountId to hash digest of files the user owns
       users: Mapping<AccountId, Vec<Hash>>,
       /// Mapping from the file hash to its owner
       files: Mapping<Hash, AccountId>,
     }
   }
   ```

   We use `#[ink(storage)]` attribute macro to tell the Rust compiler this is the smart contract storage, and the compiler will further process the storage specification here. [Check here](https://github.com/paritytech/ink#ink-macros--attributes-overview) to learn more about the macros ink! supports.
6. Now, we want our smart contract to emit events when a user successfully claims the file ownership. Let's also allow the user to forfeit the claim in the future. So the two events are:

   ```rs
   mod contract {
     // ... below the storage data structure code

     #[ink(event)]
     pub struct Claimed {
       #[ink(topic)]
       owner: AccountId,
       #[ink(topic)]
       file: Hash,
     }

     #[ink(event)]
     pub struct Forfeited {
       #[ink(topic)]
       owner: AccountId,
       #[ink(topic)]
       file: Hash,
     }
   }
   ```

   We specify two events here:

   * **Claimed**: when this event is emitted, it contains the associated user and the file digest.
   * **Forfeit**: same as above, containing the associated user and the file digest.
7. Let's work on the core logic of the smart contract

   ```rs
   mod contract {
     // ... below the event code

     impl Default for Contract {
         fn default() -> Self {
             Self::new()
         }
     }

     impl Contract {
       /// Constructor to initialize the contract
       #[ink(constructor)]
       pub fn new() -> Self {
         let users = Mapping::default();
         let files = Mapping::default();
         Self { users, files }
       }

       #[ink(message)]
       pub fn owned_files(&self) -> Vec<Hash> {
         let from = self.env().caller();
         self.users.get(from).unwrap_or_default()
       }

       #[ink(message)]
       pub fn has_claimed(&self, file: Hash) -> bool {
         self.files.get(file).is_some()
       }
     }
   }
   ```

   Here, we have defined three methods:

   * `fn new()`: the smart contract constructor. This function will be called when the smart contract is instantiated. It is marked with `#[ink(constructor)]` attribute macro right above the function.
   * `fn owned_files(&self)`: This function returns a vector, the Rust way of saying an array, of hash digests. It first retrieves the caller of the smart contract, uses it as the key to retrieve its value inside the **`users`** storage map, and returns the value. If no value is found, an empty vector is returned.

     Notice this function doesn't take an `AccountId` parameter in, so callers can only check their own file ownerships.
   * `fn has_claimed(&self, file: Hash)`: The function returns a boolean, indicating if the file specified by the hash has been claimed by another user. We read from the smart contract storage **`files`**, using the file hash as the key, and see if an owner can be retrieved. If yes, the file is claimed, and the function returns true. Otherwise, it returns false.

     Notice we still need to specify what hash function to use and how a file is converted to its hash digest. It is a job performed on the front end, so we will take care of this in the next section.
8. Now let's work on the core logic of `fn claim()`. It allows a user claims the ownership of a particular file digest. The overall logic is that:

   * We first check if the file digest has been claimed.
   * We update the storage `files` and `users` to indicate the caller claims the file ownership.
   * Emit an event that the file has been claimed so other listeners know about this.

   The following code entails the above logic:

   ```rs
   mod contract {
     // ...previous code
     impl Contract {
       // ...previous code

       /// A message to claim the ownership of the file hash
       #[ink(message)]
       pub fn claim(&mut self, file: Hash) -> Result<()> {
         // Get the caller
         let from = self.env().caller();

         // Check the hash has yet to be claimed
         if self.files.contains(file) {
           return Err(Error::AlreadyClaimed);
         }

         // Claim the file hash ownership with two write ops
         // Update the `users` storage. If a vector is retrieved, we push the hash digest into
         //   the vector. Otherwise, we create a new vector with the hash digest element inside.
         match self.users.get(from) {
           Some(mut files) => {
             // A user entry has already been built
             files.push(file);
             self.users.insert(from, &files);
           }
           None => {
             // A user entry hasn't been built, so building one here
             self.users.insert(from, &vec![file]);
           }
         }

         // Update the `files` storage
         self.files.insert(file, &from);

         // Emit an event
         Self::env().emit_event(Claimed { owner: from, file });

         Ok(())
       }
     }
   }
   ```
9. You may have noticed the return value type of `fn claim()` looks different from the two previous functions `fn owned_files()` and `fn has_claimed()`. `fn owned_files()` and `fn has_claimed()` are view functions. They only read the contract storage but don't alter it. `fn claim()`, on the other hand, is a state-modifying function. It returns a [`Result` enum type in Rust](https://doc.rust-lang.org/std/result/enum.Result.html) to indicate whether the function successfully changes the state by returning `Ok(())`, or an error occurs and the state change is reverted by returning `Err(error value here)`.

   Now, let's define the error values. Two error values will be emitted in the contract: when users try to claim a file that has already been claimed, `AlreadyClaimed`, and when users try to forfeit the file ownership they don't own, `NotOwner`.

   ```rs
   mod contract {
     // ... prev code

     // Beware that this part of the code is OUTSIDE of `impl Contract {}`, unlike the claim() function above.

     // Result type used in `fn claim()` is a short form.
     pub type Result<T> = core::result::Result<T, Error>;

     // These are two error values returned in our contract
     #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
     #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
     pub enum Error {
       /// File with the specified hash has been claimed by another user
       AlreadyClaimed,
       /// Caller doesn't own the file with the specified hash
       NotOwner,
     }

     impl Contract {
       // ... prev code
     }
   }
   ```
10. The `fn forfeit()` function basically reverses the operation of what `fn claim()` does above.

    * we first check the caller owns the file. If not, we return an error.
    * we update the **`users`** and **`files`** storage to remove the file hash digest.
    * emit an event about this new state.

    ```rs
    mod contract {
      // ...previous code
      impl Contract {
        // ...previous code
        #[ink(message)]
        pub fn forfeit(&mut self, file: Hash) -> Result<()> {
          let from = self.env().caller();

          // Check if the caller owns the file. If not, return `Error::NotOwner`.
          match self.files.get(file) {
            Some(owner) => {
              if owner != from {
                return Err(Error::NotOwner);
              }
            }
            None => {
              return Err(Error::NotOwner);
            }
          }

          // Confirmed the caller is the file owner. Update the two storage `users` and `files`.
          let mut files = self.users.get(from).unwrap_or_default();
          for idx in 0..files.len() {
            if files[idx] == file {
              files.swap_remove(idx);
              self.users.insert(from, &files);
            }
          }

          self.files.remove(file);

          // Emit an event
          Self::env().emit_event(Forfeited { owner: from, file });

          Ok(())
        }
      }
    }
    ```
11. By this point, you have completed all the core logic of the smart contract. Compile the contract with `cargo contract build` to ensure it builds. If there is any doubt about the final source code, you can always refer to the [source code here](https://github.com/CESSProject/cess-examples/blob/main/ink/poe/lib.rs).
12. After the compilation, [deploy the contract on your local cess dev chain](https://doc.cess.network/developer/smart-contract/deploy-sc-ink) and interact with the contract to test it. You can access [Contracts UI](https://contracts-ui.substrate.io/), connect it to your local node, and deploy the contract. Refer to the screenshot below.<br>

    ![Deploy PoE Ink! Contract](https://2951078474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFWgzFJTgbmbm8iaxYNRS%2Fuploads%2Fgit-blob-cd42b461df29c19ecc2ec7cd6bd5fa8064ec17d5%2Fdeploy-poe-contract.png?alt=media)

    Ensure that:

    * Contracts UI is connecting to the local CESS node.
    * You see the expected metadata when uploading the `contract.contract` file.

**Congratulation**! You have completed the smart contract development section. Before heading to the front-end development, the complete source code also contains the unit test code, the code block inside `mod tests { ... }`. We won't go over them here as they are quite self-explanatory. Please take a look. You can run them by `cargo test` command. [Check here](https://use.ink/basics/contract-testing) to learn more about contract testing.

## Front End

### Prerequisites

* Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* Install [Node v18](https://nodejs.org/en/download)
* Install [pnpm](https://pnpm.io/installation), or your favorite package manager
* Run a local development chain of the CESS node as the front end will connect to the local CESS chain. Refer here on [how to run a local CESS chain](https://doc.cess.network/developer/deploy-sc-ink#deploy-a-smart-contract).

We will start from a forked version of the Parity maintained [Substrate Front End Template](https://github.com/CESSProject/substrate-front-end-template).

```bash
cd poe-ink    # This is the root directory created during the smart contract development above.
git clone https://github.com/CESSProject/substrate-frontend-template.git frontend
cd frontend
pnpm install  # pull all the project dependencies down

# Before starting the front end below, open another terminal window and start your local CESS node.
# Refer to ./deploy-sc-ink.md#deploy-a-smart-contract

pnpm start    # start the project
```

If you see a screen similar to the following, you are good to go.

![Substrate Front End Template](https://2951078474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFWgzFJTgbmbm8iaxYNRS%2Fuploads%2Fgit-blob-94bc1e325197f434b7cc8260e81836e7cdbffcad%2Fsubstrate-frontend-template.png?alt=media)

### Before We Start

First of all, in case there is any doubt, you can always refer back to [**the entire front end source code**](https://github.com/CESSProject/cess-examples/tree/main/frontend).

To get a high-level understand of the front end template, let's refer to the second half section of [`src/App.js`](https://github.com/CESSProject/cess-examples/blob/main/frontend/src/App.js#L51):

```jsx
function Main() {
  // ...code snapped
  return (
    <div ref={contextRef}>
      <Sticky context={contextRef}>
        <AccountSelector />
      </Sticky>
      <Container>
        <Grid stackable columns="equal">
          <Grid.Row stretched>
            <NodeInfo />
            <Metadata />
            <BlockNumber />
            <BlockNumber finalized />
          </Grid.Row>
          <Grid.Row stretched>
            <Grid.Column>
              <Balances />
            </Grid.Column>
          </Grid.Row>
          <Grid.Row stretched>
            <Grid.Column width={8}>
              <Transfer />
            </Grid.Column>
            <Grid.Column width={8}>
              <Upgrade />
            </Grid.Column>
          </Grid.Row>
          <Grid.Row stretched>
            <Grid.Column width={8}>
              <Interactor />
            </Grid.Column>
            <Grid.Column width={8}>
              <Events />
            </Grid.Column>
          </Grid.Row>
          <Grid.Row stretched>
            <Grid.Column width={8}>
              <PoEWithInk />
            </Grid.Column>
            <Grid.Column width={8}>
              <PoEWithSolidity />
            </Grid.Column>
          </Grid.Row>
        </Grid>
      </Container>
      <DeveloperConsole />
    </div>
  );
}
```

Let's see how different components are laid out on the screen.

![Substrate Front End Template Components](https://2951078474-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFWgzFJTgbmbm8iaxYNRS%2Fuploads%2Fgit-blob-168ceb0f76a0b65d7a309c0a6b833c0a89c37f70%2Fsubstrate-fe-tpl-sectioned.png?alt=media)

1. The `<AccountSelector/>` component, referring to `src/AccountSelector.js`.
2. The `<NodeInfo />` component, referring to `src/NodeInfo.js`.
3. The `<Metadata />` component, referring to `src/Metadata.js`.
4. The `<Balances />` component, referring to `src/Balances.js`.

We will add a new component and showcase how to use [**use-inkathon** javascript library](https://www.npmjs.com/package/@scio-labs/use-inkathon) connecting the front end to the smart contract.

### Development

1. Add the **use-inkathon** dependency by:

   ```bash
   pnpm add @scio-labs/use-inkathon
   ```
2. In `src/App.js`, let's replace the `<TemplateModule />` component with `<PoEWithInk />`. Remove the `TemplateModule` import line and add `PoEWithInk`. We also create a basic React skeleton of `src/ProofOfExistenceInk.js`.

   So, `src/App.js` becomes:

   ```jsx
   // Remove/comment out this line
   // import TemplateModule from "./TemplateModule";
   // Add the following line
   import PoEWithInk from "./ProofOfExistenceInk";

   //... code snapped
   return (
     <div ref={contextRef}>
       { /* code snapped */ }
       <Container>
         <Grid stackable columns="equal">
           {/* code snapped */}
           <Grid.Row>
             <PoEWithInk />
           </Grid.Row>
         </Grid>
       </Container>
       {/* code snapped */}
     </div>
   )
   ```

   Open the file `src/ProofOfExistenceInk.js` and add the following code:-

   ```jsx
   import { React, useState } from "react";

   export default function PoEWithInkProvider(props) {
     return (<>Proof of Existence Ink! dApp</>);
   }
   ```

   At this point, the front end should show the line "Proof of Existence Ink! dApp".
3. From now on, we will mainly focus on the file `src/ProofOfExistenceInk.js`. We will not be adding code line by line here, but focus on the APIs provided by **use-inkathon** library that facilitate ink! smart contract interaction.

   Refer to the code [`src/ProofOfExistenceInk.js`](https://github.com/CESSProject/cess-examples/blob/main/frontend/src/ProofOfExistenceInk.js).
4. Starting from [the bottom](https://github.com/CESSProject/cess-examples/blob/main/frontend/src/ProofOfExistenceInk.js#L194-L201), we have:

   ```jsx
   <UseInkathonProvider
     appName="Proof of Existence (Ink)"
     defaultChain={cessTestnet}
     deployments={getDeployments()}
   >
     <ProofOfExistenceInk/>
   </UseInkathonProvider>
   ```

   `UseInkathonProvider` context hook provides ink! contract connection information to its children components. A config object is passed in with the name, and:

   * `defaultChain`: there are public chains with well-known IDs. As we connect to a local development chain, we set it to `cess-local`.

     ```
     export const cessTestnet = {
       network: 'cess-local',
       name: 'CESS Local',
       ss58Prefix: 11330,
       rpcUrls: [
         'http://127.0.0.1:9944',
       ],
       testnet: true,
       faucetUrls: ['https://cess.network/faucet.html'],
       explorerUrls: {
         [SubstrateExplorer.Other]: `https://substats.cess.network/`,
       },
     }
     ```
   * `deployments`: takes an object with contract information, such as contractId, networkId, abi and contract address.

     ```
     const getDeployments = () => {
       let developments = [
         {
           contractId: 'poe-ink-contract',
           networkId: cessTestnet.network,
           abi: metadata,
           address: CONTRACT_ADDR,
         },
       ];
       return developments;
     }
     ```

   With `UseInkathonProvider`, we can make ink! API calls inside `<ProofOfExistenceInk />` component.
5. Looking at the code inside [`function ProofOfExistenceInk(props) {...}`](https://github.com/CESSProject/cess-examples/blob/main/frontend/src/ProofOfExistenceInk.js#L165)

   ```jsx
   // NOTE: In `examples/poe-ink/contract` directory, compile your contract with
   //   `cargo contract build`.
   import metadata from "../../ink/poe/target/ink/poe_ink_contract.json";

   // NOTE: Update your deployed contract address below.
   const CONTRACT_ADDR = "cXjN2RG7YEpxx1bCa4zJKy3igsh3DuEo8bHnfKp1KsH5LaUub";


   const ProofOfExistenceInk = () => {
     // get RPC api and active account
     const { api, activeAccount } = useInkathon();
     const { freeBalanceFormatted } = useBalance(activeAccount?.address);

     const developments = getDeployments();

     // Register the contract and get contract object
     const { contract: poeContract } = useRegisteredContract(developments[0].contractId);
     const [fileHash, setFileHash] = useState(null);
     const [ownedFilesRes, setOwnedFilesRes] = useState([]);

     //... code snapped
   }
   ```

   * Use `useInkathon()` to get the current active account and rpc api.
   * Use `useBalance(account)` to get the account's current balance.
   * Use `useRegisteredContract(contractId)` to get the contract ABI.

At this point, we have a contract instance `poeContract` that we can interact with.

```
- We then use `contractQuery()` to query the value returning from `ownedFiles()` function from the smart contract. Recall from the previous section that this function returns all the file hash digests the user account owned.

- Then we use `decodeOutput()` method to decode the result, converting from the chain data types to javascript data types.

All the functions mentioned here are provided by **use-inkathon**. You can learn more about their usage in [**use-inkathon documentation**](https://www.npmjs.com/package/@scio-labs/use-inkathon?activeTab=readme).
```

6\. Here, we specify how the file hash digest is computed.

````
```jsx
const computeFileHash = (file) => {
  const fileReader = new FileReader();
  fileReader.onloadend = (e) => {
    // We extract only the first 64kB  of the file content
    const typedArr = new Uint8Array(fileReader.result.slice(0, 65536));
    setFileHash(blake2AsHex(typedArr));
  };
  fileReader.readAsArrayBuffer(file);
};
```

We use [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader) provided in modern-day browser JS APIs to read the uploaded file, extract the first 64 kB, and use `blake2AsHex()` [Blake2 cryptographic hash function](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2) to calculate its hash digest provided by [**@polkadot/util-crypto**](https://polkadot.js.org/docs/util-crypto/examples/hash-data) library.
````

7\. We then implement a few helper components **TxButton**, **ConnectWallet**, and **WalletSwitcher** to display the UI.

```
- **TxButton** component sends either `claim()` or `forfeit()` transaction to the chain depending on whether the user owned the file. It uses `contractTx()` to construct and send the transaction.

- **ConnectWallet** component allows users to switch from different wallet providers, including Enkrypt, Polkadot.js extension, SubWallet, and Talisman. A typical choice would be to use [Polkadot.js extension](https://polkadot.js.org/extension/).<br/>

    ![Our Front end supports multiple wallet providers](../../assets/developer/tutorials/poe-ink/wallet-type.png)

    It uses `connect()` function from `useInkathon()` and `allSubstrateWallets()` to get the information of all supported wallets.

- **WalletSwitcher** component retrieves all available accounts provided by the wallet chosen in **ConnectWallet**, using the `accounts` object. It also uses `setActiveAccount()` to set a particular account and `disconnect()` to disconnect from the chosen wallet.
```

8\. Finally, we have a front end similar to the following:<br>

```
![Proof of Existence Front end](../../assets/developer/tutorials/poe-ink/full-frontend.png)
```

## Tutorial Completion

**Congratulation**! Let's recap what we have done in this tutorial:

* We have successfully implemented a PoE logic in ink! smart contract and deploy it on a local CESS node.
* Starting with the Substrate Front End Template and **use-inkathon** React library, we have successfully implemented the front end that interacts with the smart contract.

Now, you can build your dApps and deploy them on the CESS testnet to test it out. For the next step, you can also learn how to [develop a dApp with Solidity smart contract](https://doc.cess.network/developer/smart-contract/poe-solidity) as well.

### References

* [Ink! 5.0](https://use.ink/)
* [CESS Node](https://github.com/CESSProject/cess)
* [Substrate Front End Template](https://github.com/CESSProject/substrate-frontend-template)
* [Substrate Contracts UI](https://contracts-ui.substrate.io/)
