Part I - Build the DApp | Developers

Part I - Build the DApp

Estimated reading time: More than 10 minutes

The Dapp in this tutorial is deliberately unfinished for training purposes. Let’s implement the missing functionalities one by one, testing them as we go along. The following steps add primary and standard functionalities like signing typed data, signing a message, and sending a transaction. Go to your browser with each step to test your DApp to ensure everything works fine.

1- Fetch the messages

First, let’s write the logic to fetch the last ten messages. This is implemented in the useFetchMessages custom hook in src/api/useFetchMessages.ts.

Look up the hook in the wagmi documentation to call a read-only method. Don’t hesitate to look at the contract Application Binary Interface (ABI) in src/utils/contract.json to find what contract method to call. Once implemented, your DApp fetches and displays existing messages.

If there are no messages displayed, please check

  1. The Metamask Extention is enabled
  2. You have signed in to Metamask: Click on the icon to the right of “Ledger Guest Book”. It must say “Disconnect.”
  3. Connect to the Account:
    • Click on the Extension icon,
    • then the Round Icon (top right)
    • Choose the account

Test: Fetch the messages

The latest messages are displayed automatically

import artifacts from "../utils/contract.json";
import { domain } from "../utils/EIP712";
import { useEffect } from 'react';
import { useContractRead } from "wagmi";

/* STEP #1
    TODO: Develop the logic to fetch the last 10 messages
    TIP: There is a hook in the Wagmi documentation to call the read-only method
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/
const useFetchMessages = () => {
  const { data, isError, isLoading, refetch } = useContractRead({
    addressOrName: domain.verifyingContract,
    contractInterface: artifacts.abi,
    functionName: 'getLast10Messages',
  });

  useEffect(() => {
    // fetch once (on mount) client-side only 
    refetch();
  }, []);

  return { data, isError, isLoading };

};

export default useFetchMessages;

2- Sign EIP-712 typed data (a user message)

Now that you have fetched the existing messages, let’s add new ones to the list! A message is typed data (cf. EIP-712). It needs to be signed before being broadcast. The first step is to write the logic to sign typed data with the connected account. This is implemented in the usePostMessage custom hook in src/api/usePostMessage.ts. There is a hook in the wagmi documentation to do an EIP-712 signature, find it and implement it.

3- Post a message

Now that you have a good message let’s send this message to the smart contract. Sending your message to the smart contract consists of writing data to your smart contract. This is also implemented in the usePostMessage custom hook in src/api/usePostMessage.ts. A hook from the wagmi documentation allows you to call a write method.

Don’t forget to return the errors.

import { useContractWrite, useSignTypedData } from "wagmi";
import artifacts from "../utils/contract.json";
import { domain, types } from "../utils/EIP712";

const WRITE_ASYNC_OVERRIDES_GASLIMIT = { gasLimit: 250_000 };

/* STEP #2
    TODO: Develop the logic to sign typed data with connected account
    TIP: There is a hook from the Wagmi documentation to make a 712 signature
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/

/* STEP #3
    TODO: Develop the logic to post a message
    TIP: There is a hook from the Wagmi documentation to call the write method
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/
const usePostMessage = () => {
  const { data, isError, isLoading, isSuccess, error, writeAsync } = useContractWrite({
    addressOrName: domain.verifyingContract,
    contractInterface: artifacts.abi,
    functionName: 'sendMessage',
  });
  const { isError: is712Error, isSuccess: is712Success, error: error712, signTypedDataAsync } = useSignTypedData();

  const postMessage = async (message: string, author: string) => {
    try {
      const value = { contents: message, from: author };
      const signature = await signTypedDataAsync({ value, domain, types });
      const tx = await writeAsync({ args: [message, signature], overrides: WRITE_ASYNC_OVERRIDES_GASLIMIT, });
      console.log(`tx hash: ${tx.hash}`);
    } catch (e) {
      throw new Error(e);
    }
  };

  return {
    postMessage,
    data,
    error: error || error712,
    isError: isError || is712Error,
    isLoading: isLoading || isLoading,
    isSuccess: isSuccess && is712Success
  };
};

export default usePostMessage;

Test: Sign and Post EIP-712-typed data

  1. Enter a new message in the text field
  2. Click on the arrow, to send the message
  3. In the browser, a Signature Request modal opens.
    • Click on the Sign Button (or Cancel to terminate the operation)
  4. In the browser, a new Confirmation modal with the estimated gas fees opens.
    • Click on the Confirm Button (or Cancel to terminate the operation)
  5. In the main browser window, under the Write a message field,
    • Verify the Message is sent (Green text: “Message sent!”)
    • Refresh
    • Verify the Message is now in the History list (this step can takes few seconds as the transaction needs to be added to a minted block)
Note
You can see this on goerli.etherscan.io. Verify the transaction, using the using your Ethereum address in Metamask https://goerli.etherscan.io/address/

4- Sign an EIP-191 message

Let’s allow users to ‘like’ other posts. ‘Liking’ a post consists in signing a message (cf. EIP-191) and then calling a method of the smart contract. It’s similar to what you did above to create a post, but the user signs an EIP-191 message instead of EIP-712 typed data (learn more about these specifications here).

The message to sign is the following: I like the post #${id} posted by ${author}; This is implemented in the useLikeMessage custom hook in src/api/useLikeMessage.ts . There is a hook from the wagmi documentation to sign a message.

5- ‘Like’ a message

Once the user has successfully signed the message, let’s share that a user has liked the associated post by calling a smart contract method. This step is very similar to Step #3, where you posted a message, the main difference being the method to call on the smart contract (and eventually its arguments). This is also implemented in the useLikeMessage custom hook in src/api/useLikeMessage.ts.

Don’t forget to return the errors.

Test: Sign and ‘Like’ an EIP-191 message

  1. Click on the heart of a message
  2. In the browser, a Signature Request modal opens.
    • Click on the Sign Button (or Cancel to terminate the operation)
  3. In the browser, a new Confirmation modal with the estimated gas fees opens.
    • Click on the Confirm Button (or Cancel to terminate the operation)
  4. In the main browser window,
    • Wait a few moments before clicking Refresh (See note below)
    • Verify Heart is updated
Note
The transaction takes time for the network to be updated. You can see this on goerli.etherscan.io. Verify the transaction, using the using your Ethereum address in Metamask https://goerli.etherscan.io/address/

import { useSignMessage, useContractWrite } from 'wagmi'
import { domain } from "../utils/EIP712";
import artifacts from "../utils/contract.json";

const WRITE_ASYNC_OVERRIDES_GASLIMIT = { gasLimit: 250_000 };

/* STEP #4
    TODO: Develop the logic to sign a message
    TIP: There is a hook from the Wagmi documentation to sign message (not typed ones)
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/

/* STEP #5
    TODO: Develop the logic to like a message
    TIP: There is a hook from the Wagmi documentation to call the write method
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/
const useLikeMessage = () => {
  const { data, isError, isLoading, isSuccess, error, writeAsync } = useContractWrite({
    addressOrName: domain.verifyingContract,
    contractInterface: artifacts.abi,
    functionName: 'likeMessage',
  });
  const { error: erroSign, isError: isSignError, isLoading: isSignLoading, isSuccess: isSignSuccess, signMessageAsync } = useSignMessage();


  const likeMessage = async (id: string, author: string) => {
    try {
      const signature = await signMessageAsync({ message: `I like the post #${id} posted by ${author}` });
      console.log({ signature })
      const tx = await writeAsync({ args: [id], overrides: WRITE_ASYNC_OVERRIDES_GASLIMIT, });
      console.log(`tx hash: ${tx.hash}`);
    } catch (e) {
      throw new Error(e);
    }
  };

  return {
    likeMessage,
    data,
    error: error || erroSign,
    isError: isError || isSignError,
    isLoading: isLoading || isSignLoading,
    isSuccess: isSuccess && isSignSuccess
  };
};

export default useLikeMessage;

6- Tip an author via a transaction

The last step consists of tipping an author to thank him for his contribution. Technically, this means sending a transaction to the author’s address. This is implemented in the useRewardAuthor custom hook in src/api/useRewardAuthor.ts. As before, you will find a hook from the wagmi documentation to send transactions.

Don’t forget to return the errors.

Test: Tip an author via a transaction

  1. Click on the Tip of a message
  2. In the browser, a Signature Request modal opens.
    • a. Click on the Confirm Button (or Cancel to terminate the operation)
  3. In the main browser window,
    • a. Wait a few moments (See note below)
  4. On goerli.etherscan.io verify the transaction, using using your Ethereum address in Metamask https://goerli.etherscan.io/address/<Address>
Note
The transaction takes time for the network to be updated. You can see this on goerli.etherscan.io. Verify the transaction, using the using your Ethereum address in Metamask https://goerli.etherscan.io/address/

import { useSendTransaction } from 'wagmi';
import { ethers } from "ethers";

/* STEP #6
    TODO: Develop the logic to send a transaction
    TIP: There is a hook from the Wagmi documentation to send a transaction
    Link of the documentation: https://wagmi.sh/docs/getting-started
*/
const useRewardAuthor = () => {
  const { data, isIdle, isError, isLoading, isSuccess, sendTransactionAsync } =
    useSendTransaction()

  const rewardAuthor = async (author: string) => {
    const tx = await sendTransactionAsync({
      request: {
        to: author,
        value: ethers.BigNumber.from('1000000000000000'), // 0.001 ETH
      }
    });
    console.log(`tx hash: ${tx.hash}`);
  };

  return { data, isError, isLoading, isSuccess, isIdle, rewardAuthor };
};

export default useRewardAuthor;

Video

  • Step 1: Fetch messages (at 01:12)
  • Step 2: Sign an EIP-712 typed data (a user message) (at 07:05)
  • Step 3 and 4: Post and sign an EIP-191 message (at 09:44)
  • Step 5: ‘Like’ a message (at 16:08)
  • Step 6: ‘Tip’ an author using a transaction (at 24:20)

Did you find this page helpful?


How would you improve this page for developers?



Introduction
Part II - Let’s add the DApp to Ledger Live
Getting Started
Theme Features
Customization

Live App