Master Solidity 1 - Simple Storage

Posted on July 15, 2023

1. Simple Storage

Before we start, if you want to watch a video instead of reading this article, CLICK HERE

Don’t get it twisted. This is NOT a Solidity 101 course, but HOW I used phenomenal resources like Patrick Collin’s course or Solidity by Example to master the basics in Solidity

Here’s the TL;DR

I share:

  1. My notes on the basics in Solidity that you need to know for this smart contract
  2. My exercises
  3. How I practised to deploy and test with this smart contract
  4. How I build a front end with this smart contract

Context

Courses like Patrick Collin’s course and Solidity by Example are GOLD. Legit never has it been easier to learn Solidity. Especially for someone like me who get’s scared learning anything new. I’m talking about proper anxiety with a sprinkle of conviction I will never be good at programming. Enough about me.

When I first saw the explanation I understood the code. But my personal wish was, that I was able to write up such a basic contract, without looking up how to write a Struct or how to write up a contract.

Practise is the answer. I learn in a very old fashioned way. I’m old. The way we used to learn in school is by just rewriting the outcome a bunch a time. Now that is inefficient. However the way that I learned math, was perfect. Which was:

  1. Here’s the theory
  2. Here are 20 exercises

By doing the exercises, my level of understanding of the theory went to a deeper level. Today when someone asks me to write a Simple Storage contract or an Ether Wallet, I don’t need to look anything up.

How to use this article

When I first dove into Solidity, I was looking for syntax practise. So I’m starting to dissect simple contracts, building on the knowledge I learned from a course. Then come up with exercises (powered by gpt), just like math, to get to a deeper level of understanding. Finally come back to the contract. Here’s a breakdown of what this article will give you:

  1. Take a contract and destruct what it’s aiming to do.
  2. Bring it back to the fundamental Solidity concepts, I should understand to master this contract
  3. Practise the fundamentals
  4. Then build the contract without looking at it

The contract

Ok, so this is the contract that you learned about in the course. Awesome.

Le code:

// I'm a comment!
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

// pragma solidity ^0.8.0;
// pragma solidity >=0.8.0 <0.9.0;

contract SimpleStorage {
    uint256 myFavoriteNumber;

    struct Person {
        uint256 favoriteNumber;
        string name;
    }
    // uint256[] public anArray;
    Person[] public listOfPeople;

    mapping(string => uint256) public nameToFavoriteNumber;

    function store(uint256 _favoriteNumber) public {
        myFavoriteNumber = _favoriteNumber;
    }

    function retrieve() public view returns (uint256) {
        return myFavoriteNumber;
    }

    function addPerson(string memory _name, uint256 _favoriteNumber) public {
        listOfPeople.push(Person(_favoriteNumber, _name));
        nameToFavoriteNumber[_name] = _favoriteNumber;
    }
}

This is my visual of what the contract does:

Screenshot 2023-07-01 at 12.38.08.png

What does the contract DO

Breaking it down simple. I like to look at the functions and then use that angle to define what the contract does. Looking at the above visual we have 4 functions:

  1. Store: change the value of the variable of favoriteNumber
  2. Retrieve: get the favourite number of a person
  3. addPerson: add a person to the list with their favourite number

Solidity syntax I should know

For this level of contract I need to be familiar with:

  1. Variables and typing (type + visibility)
  2. Structs
  3. Arrays
  4. Mapping
  5. Basics functions

My notes on Solidity

Cool now I knew what the contract should do and what basics are involved. Next I focused on the basic knowledge of Solidity and rewriting it in my own words. Elaborate, I know.

Variables

So for this contract we’re just using basic variables, strings and integers. If you’re looking for notes on variables, you can find my notes here.

Here’s a snippet:

  1. Public: An variable marked as public can be accessed by anyone, both inside and outside the contract.
  2. Private: A private variable is only accessible within the contract itself.
  3. Internal: An internal variable can be accessed by other contracts derived from the same contract.
  4. External: An external variable is meant to be called from external contracts or accounts, not within the contract itself.

I know it might seem “extensive”, but I literally tried to memorise what each of these do. At some point if you build “enough”, it becomes second nature to knowing what each visibility keyword does. But at the START, it really was daunting to me. So I took time to go over these. In my experience, it goes a long way to memorise what each visibility does. Also I like to take baby steps.

Q: Define a variable

uint256 myFavoriteNumber;

I know this is very elaborate. But this is how I learn. It’s not for everyone.

Now comes the time where I did brain dead stuff. Type out every visibility. Write behind each variable in comments what the visibility allows. Here is where I learn.

uint256 public owner; //variable that anyone can see
uint256 internal owner; //if a contract creates another contract and the newly created contract wants to interact with this variable in the parent contract, it is allowed to do so. 
uint256 external owner; //cant be accessed from within contract, only external contracts or accounts
uint256 private owner; //cant be accessed by external contract or account, only from within contract

Structs

OK, so I remember back in the days, I found structs tricky. Especially combining them with lists. And here is where mindless practise really helped me. So here’s a short explanation and then we dive into practise of just Structs. Finally we quickly move on to how to use structs and lists combined.

In a simple way, a struct is a template. It’s a way to define a custom data type that can hold multiple related variables. It allows you to create complex data structures to organise and store different pieces of information.

This is how I visualise a Struct. Imagine a Spreadsheet, where you name the column names and the sheet. The column names are the variables you can use inside a struct and the spreadsheet name is the name of the struct. Here’s what that looks like:

struct Token {
		string name;
		string symbol;
		uint256 totalSupply;
}

And here is the spreadsheet:

Screenshot 2023-06-19 at 12.57.15.png

So now this template can be used to fill in data. But it’s just a template, so in order to ‘store’ data, we need something like a variable or list, that can make use of this template or struct.

contract myToken{
		//define a struct
		struct Token{
				string name;
				string symbol;
				uint256 totalSupply;
		}
	//use the struct for a variable
	Token public razaCoin //so now 'razaCoin' uses 'Token' as a template
}

Let’s assign some data using this template

pragma solidity ^0.8.0;

contract myToken {
    struct Token {
        string name;
        string symbol;
        uint256 totalSupply;
    }

    Token public razaCoin;

    function setTokenValues() external {
        razaCoin.name = "RazaCoin";
        razaCoin.symbol = "RZC";
        razaCoin.totalSupply = 1000000;
    }
}

So inside a sheet this looks like this. Now for a variable, there is only 1 row, if we want an example with multiple rows, we could use a list. That’s for later.

Screenshot 2023-06-19 at 13.06.57.png

Arrays

An array is a list. Simple as. You can store items in a list, just like Python and Javascript.

Defining an array is similar to defining a variable.

TYPE+’[]’+VISIBILITY+NAME

Example:

uint256[] public userAccounts;

So an array is like a container that holds related pieces of data.

Once a Struct is defined, you can use it to type a list like this:

Person[] public listOfPeople;

Now this list can be filled with the template we defined. So to visualise it means, we can add ‘rows’ to our sheet. Each row we fill out a column. We use the push method for that, but more on that later.

Mapping

Mapping in Solidity is like a dictionary or a phone book that helps you pair one value with another. It allows you to store and access information based on an unique key.

For a mapping the structure is as follows:

mapping(KEY ⇒ VALUE) + VISIBILITY + NAME

Example:

mapping(address => uint256) public balances

Here’s how I visualise it:

Screenshot 2023-06-20 at 19.37.03.png

Note, that a mapping is not indexed. You retrieve a value, by using the key. So you can’t use a number to retrieve a value from a mapping like this:

balances[0]

Basic Functions

Functions take input do something with it and return an output. Functions felt a lot more complex than doing them in Python. You got modifiers, you type the parameters, you need to tell the type of value it returns sometimes. For this contract you need to be familiar with:

  1. Typing Parameters
  2. Visibility & Scope
  3. Understanding how to return a value
  4. Memory

I’m just glancing over my notes. If you’ve been watching tutorials though, you will for sure have touched upon the basics. So here are exercises specifically to functions that I practised. Yes they were created by chatGPT.

  1. Exercise 1: Implement a function that takes two integers as parameters and returns their sum.
  2. Exercise 2: Implement a function that takes a number as a parameter and returns its square.

The answers:

You better not be looking at the answers before trying. For real though, this is the level you should be able to crush. If not, now you know where your weakness lies. Work on it.

  1. Exercise 1: Implement a function that takes two integers as parameters and returns their sum.
pragma solidity ^0.8.0;

contract Exercise1 {
    function addNumbers(uint256 _a, uint256 _b) external pure returns (uint256) {
        return _a + _b;
    }
}
  1. Exercise 2: Implement a function that takes a number as a parameter and returns its square.
pragma solidity ^0.8.0;

contract Exercise2 {
    function calculateSquare(uint256 _number) external pure returns (uint256) {
        return _number * _number;
    }
}

OK. That’s basically it for the fundamentals in syntax. Now I didn’t address everything for a simple reason, because I’m assuming you watch an actual tutorial before you came here. These are just my notes for when I was going through the course and served as a refresher before I dove into exercises.

Exercises

Basic Functions

Exercise 1: Define a function named "addNumbers" that takes two uint256 arguments and returns their sum. Code:

pragma solidity ^0.8.0;

contract BasicFunctionsExercise {
    function addNumbers(uint256 _a, uint256 _b) public pure returns (uint256) {
        return _a + _b;
    }
}

Exercise 2: Declare a function named "sayHello" that does not take any arguments and returns a string "Hello, World!". Code:

pragma solidity ^0.8.0;

contract BasicFunctionsExercise {
    function sayHello() public pure returns (string memory) {
        return "Hello, World!";
    }
}

Exercise 3: Define a function named "isEven" that takes an integer as an argument and returns a boolean indicating whether the number is even or not. Code:

pragma solidity ^0.8.0;

contract BasicFunctionsExercise {
    function isEven(uint256 _number) public pure returns (bool) {
        return _number % 2 == 0;
    }
}

Variables and Typing

Exercise 1: Declare a variable of type integer with the name "age" and assign it a value of 25 with a function. Code:

pragma solidity ^0.8.0;

contract VariablesExercise {
    uint256 public age;

    function setAge() public {
        age = 25;
    }
}

Exercise 2: Declare a variable of type string with the name "name" and assign it the value "Alice" with a function. Code:

pragma solidity ^0.8.0;

contract VariablesExercise {
    string public name;

    function setName() public {
        name = "Alice";
    }
}

Exercise 3: Declare a private variable of type boolean with the name "isStudent" and assign it the value true with a function. Code:

pragma solidity ^0.8.0;

contract VariablesExercise {
    bool private isStudent;

    function setIsStudent() public {
        isStudent = true;
    }
}

Structs

Exercise 1: Define a struct named "Person" with two fields: "name" of type string and "age" of type uint256. Write a function that uses the Struct Person for a variable with the values: “Alice” and “25”. Code:

pragma solidity ^0.8.0;

contract StructsExercise {
    struct Person {
        string name;
        uint256 age;
    }

    Person public person;

    function setPerson() public {
        person = Person("Alice", 25);
    }
}

Exercise 2: Declare an array of type "Person" with the name "people" and initialise it with two Person structs. Push 2 “people” with their age to the list: Alice &25, Bob & 30. Code:

pragma solidity ^0.8.0;

contract StructsExercise {
    struct Person {
        string name;
        uint256 age;
    }

    Person[] public people;

    function setPeople() public {
        people.push(Person("Alice", 25));
        people.push(Person("Bob", 30));
    }
}

Exercise 3: Define a function named "getPersonName" that takes a Person struct as an argument and returns the name of the person. Code:

pragma solidity ^0.8.0;

contract StructsExercise {
    struct Person {
        string name;
        uint256 age;
    }

    function getPersonName(Person memory _person) public pure returns (string memory) {
        return _person.name;
    }
}

Arrays

Exercise 1: Declare a dynamic array of type uint256 with the name "numbers" and initialise it with three numbers: 1, 2, and 3 using a function. Code:

pragma solidity ^0.8.0;

contract ArraysExercise {
    uint256[] public numbers;

    function setNumbers() public {
        numbers = [1, 2, 3];
    }
}

Exercise 2: Define a function named "sumArray" that takes an array of uint256 as an argument and returns the sum of all the numbers in the array. This might be a spicy one. Code:

pragma solidity ^0.8.0;

contract ArraysExercise {
    function sumArray(uint256[] memory _array) public pure returns (uint256) {
        uint256 sum = 0;
        for (uint256 i = 0; i < _array.length; i++) {
            sum += _array[i];
        }
        return sum;
    }
}

Exercise 3: Declare a fixed-size array of type address with the name "accounts" and initialise it with three Ethereum addresses using a function. Code:

pragma solidity ^0.8.0;

contract ArraysExercise {
    address[3] public accounts;

    function setAccounts() public {
        accounts = [address(0x123), address(0x456), address(0x789)];
    }
}

Mapping

Exercise 1: Declare a mapping named "balances" that maps addresses to uint256 values. Write a function to set the balance of an address. Code:

pragma solidity ^0.8.0;

contract MappingExercise {
    mapping(address => uint256) public balances;

    function setBalance(address _address, uint256 _balance) public {
        balances[_address] = _balance;
    }
}

Exercise 2: Define a function named "getBalance" that takes an address as an argument and returns the balance associated with that address from the "balances" mapping. Code:

pragma solidity ^0.8.0;

contract MappingExercise {
    mapping(address => uint256) public balances;

    function getBalance(address _address) public view returns (uint256) {
        return balances[_address];
    }
}

Exercise 3: Declare a mapping named "studentGrades" that maps strings (student names) to uint256 values (grades). Write a function that sets the grade of a student. Code:

pragma solidity ^0.8.0;

contract MappingExercise {
    mapping(string => uint256) public studentGrades;

    function setGrade(string memory _studentName, uint256 _grade) public {
        studentGrades[_studentName] = _grade;
    }
}

Final Task

This part is simple. Based on the exercises you should be able to:

  1. Create a contract
  2. Define a Struct
  3. Use the Struct in an Array
  4. Create a function to:
    1. Add a name with a favourite number to the list
    2. retrieve the favourite number of a person
    3. store a value for a variable like favoriteNumber

Here’s a blank template with comments. Write it out.

// I'm a comment!
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

// pragma solidity ^0.8.0;
// pragma solidity >=0.8.0 <0.9.0;

//define the contract

//define a variable for a favorite number

//write a struct for a Person containing favorite number and a name

//write an array containing the list of people. hint use the struct to type the array

//write a mapping to link the name to the favorite number

//write a function to store the favorite number

//write a function to retrieve the favorite number

//write a function to add a person to the list of people

I added a function to the contract to retrieve the whole list. Try it out yourself.

Here’s my code (it’s fine if you glanced at it, just switch tabs and write it out yourself)

function retrieve() public view returns (Person[] memory) {
        return listOfPeople;
    }

Using Developer Tools

Remix is great, but pretty much straight away I started hands on with deployment frameworks. The one’s I use are Brownie and Truffle.

I like Brownie better, because it’s Python. Python is much cleaner and easier to use. Having said that, probably most devs will opt for JavaScript. Then my preferred tool is Truffle, because of Truffle dashboard.

Truffle

OK, let’s install Truffle and then use it to write some basic test and then deploy it. If you’ve never touched Truffle, no worries I got you.

Install Truffle

Truffle is a popular development environment, testing framework, and asset pipeline for Ethereum. To deploy with Truffle, follow the steps below:

Install Truffle globally using npm:

npm install -g truffle

You can run the following command to create a starter project:

truffle unbox metacoin

If you want to create a complete blank project run:

truffle init

Remove the old contracts and test files that came with the Metacoin project. Replace the old contract file name with your contract. This is the new contract you're deploying.

Configure Truffle

We’re going to use Truffle Dashboard, what that is, I’ll show in a minute. Truffle Dashboard should work out of the box, but just in case it doesn’t…adjust your truffle.config.js file like this:

// Configure your compilers
  compilers: {
    solc: {
      version: "0.8.17",      // Fetch exact version from solc-bin
    }
  },
  dashboard: {
    port: 24013,
    host:"localhost" ,
  },

  networks: {
    // ... network configurations, including the network named 'dashboard'

Now you if you know a little bit dev tools, you might be asking, where do i pass the rpc url. Well you don’t have to. Let’s take a look.

Deploy

Normally you test first, then deploy. But if you’re starting out, you might not be interested in tests. So we’ll do deploying first and then dive into tests. We’re going to need two windows in your terminal. In the first we run Truffle Dashboard, which spins up a local host server. The second where we will compile the contract and send it to the dashboard to deploy.

Terminal 1: Run dashboard

truffle dashboard

Go to your browser (if it doesn’t pop up automagically) and the dashboard should be running on localhost:24013

Connect your wallet. I use MetaMask, and connect to the network you want to use.

Terminal 2: Compile and send contract

truffle migrate --network dashboard

Now when you run the above command, go to your dashboard and you should see the metadata of your contract. Make sure your wallet is connected and you’re on the desired network (probably a test net). Click deploy and that’s it!

Test your contract

Tests basically run a function and check if the function returns the desired outcome. So you need to tell a test what function to run, the parameters(if any) and then what the desired outcome is. That’s it.

In the test folder create a file. I usually stick with the name of the contract so: simpleStorage.js

Use the following command in your terminal to run the tests:

truffle test

If you want to run a specific test file use:

$ truffle test ./path/to/test/file.js

Here are some example tests. Please note, be careful with GPT. I tried it with GPT and the first couple of tests were actually not working.

// SPDX-License-Identifier: MIT

const SimpleStorage = artifacts.require('SimpleStorage');

contract('SimpleStorage', (accounts) => {
  let simpleStorage;

  before(async () => {
    simpleStorage = await SimpleStorage.deployed();
  });

  it('should store the favorite number', async () => {
    const favoriteNumber = 42;
    await simpleStorage.store(favoriteNumber);
    const storedFavoriteNumber = await simpleStorage.retrieve();
    assert.equal(storedFavoriteNumber, favoriteNumber, 'Favorite number was not stored correctly');
  });

  it('should add a person to the list', async () => {
    const name = 'Alice';
    const favoriteNumber = 7;
    await simpleStorage.addPerson(name, favoriteNumber);
    const person = await simpleStorage.listOfPeople(0);
    assert.equal(person.favoriteNumber, favoriteNumber, 'Favorite number of the person was not added correctly');
    assert.equal(person.name, name, 'Name of the person was not added correctly');
  });

  it('should map a name to a favorite number', async () => {
    const name = 'Bob';
    const favoriteNumber = 10;
    await simpleStorage.addPerson(name, favoriteNumber);
    const mappedFavoriteNumber = await simpleStorage.nameToFavoriteNumber(name);
    assert.equal(mappedFavoriteNumber, favoriteNumber, 'Favorite number was not mapped correctly');
  });
});

Brownie

Brownie is a Python framework for Ethereum smart contract testing, debugging, interaction and deployment.

Install Brownie

You can make use of the pipx library. It makes it easier to create a virtual environment on the fly. A virtual environment is just a sandbox on your computer, where you can install stuff locally, instead of globally. So its a closed play area. However I like to do it the standard way, which is creating your own python virtual environment and then installing everything. Just works everytime.

python3 -m venv ./myv
source ./myv/bin/activate

Then you can go ahead and use the pip to install brownie and the dotenv library at the same time.

pip install python-dotenv eth-brownie

Set up a new Brownie project with a standard ERC20 smartcontract

brownie bake token
cd token

Rename the contract to simpleStorage.sol and paste the code in there.

Configure Brownie

There are different ways to add networks to Brownie. I like to do it like this.

Create a network-config.yaml file with the Scroll Alpha Testnet information.

live:
- name: Ethereum
  networks:
  - chainid: 534353
    explorer: https://blockscout.scroll.io/
    host: https://scroll-alphanet.public.blastapi.io
    id: testnet-scroll
    name: Testnet (scroll)

Import the network configuration and create a new account with your private key. my-new-account is the name I gave, you can pass any name you want here. After this prompt, you will be asked to input the private key and choose a password.

brownie networks import ./network-config.yaml
brownie accounts new my-new-account

Deploy with Brownie

Run the following command:

brownie compile

Make sure to adjust the deploy.py script. Our contract doesn’t take in any parameters to deploy, so we can remove everything, except for the account part.

from brownie import SimpleStorage, accounts

account = accounts.load("my-new-account")

def main():
    return SimpleStorage.deploy({'from': account})

Now go to your terminal and run:

brownie run simplestorage.py --network testnet-scroll

That’s it

Test with Brownie

The following tests, go into the test folder. They are the same as the JavaScript tests, but instead written in Python. Make sure you have ganache installed, which is a local blockchain to test stuff:

npm install -g ganache

As for the tests:

import pytest
from brownie import SimpleStorage, accounts

def test_simple_storage():
    # Deploy the contract
    simple_storage = SimpleStorage.deploy({'from': accounts[0]})

    # Test storing and retrieving a favorite number
    favorite_number = 42
    simple_storage.store(favorite_number, {'from': accounts[0]})
    stored_favorite_number = simple_storage.retrieve({'from': accounts[0]})
    assert stored_favorite_number == favorite_number, "Favorite number was not stored correctly"

    # Test adding a person to the list
    name = "Alice"
    favorite_number = 7
    simple_storage.addPerson(name, favorite_number, {'from': accounts[0]})
    person = simple_storage.listOfPeople(0, {'from': accounts[0]})
    print(person)
    assert person[1] == name, "Name of the person was not added correctly"
    assert person[0] == favorite_number, "Favorite number of the person was not added correctly"
    
    # Test mapping a name to a favorite number
    name = "Bob"
    favorite_number = 10
    simple_storage.addPerson(name, favorite_number, {'from': accounts[0]})
    mapped_favorite_number = simple_storage.nameToFavoriteNumber(name, {'from': accounts[0]})
    assert mapped_favorite_number == favorite_number, "Favorite number was not mapped correctly"

Quick note

A quick note about the test. You will notice when comparing the javascript test with the python test, that in the second test person is accessed differently. And that has to do with the following two lines of code:

//truffle
const person = await simpleStorage.listOfPeople(0);

and for python:

person = simple_storage.listOfPeople(0, {'from': accounts[0]})

If you will console.log and print both of these in your terminal, you will see different outputs. That is because in Python the return is an array of values, whereas its an object in JavaScript. Would the return value have been a dictionary in Python, you could have used the person.favoriteNumber instead of person[0].

How to build a dApp with this

My goal was to go end to end. Building a smart contract was just a part of what I want to do. The cool thing is, that with simple smart contracts like this, it makes everything easier. From deploying, to tests, to building out the front end. So that’s what I’m sharing in this section. How to build out a frontend for this smart contract. We’re going to use 2 frameworks for that:

  1. Ethers
  2. NextJS

I love nextJS, its easy to build with and easy to deploy. This is how I interact with the smart contract in a react framework like NextJS. Here is the Github link if you want to go over the code.

Install dependencies

I’m going to assume you have nodeJS installed by now. If not…Google.

I created a new project with:

npx create-next-app

I didn’t use TS, because I don’t like torturing myself and I used Next 13 with the App Router. Quick disclaimer, you can still choose to not use the App Router. All you need to do is, copy paste whatever is in the page.jsx file to index.jsx. I’ll write about it again in a bit. This is what the choices look like. Again I chose NO for TypeScript and src/ directory.

Screenshot 2023-07-15 at 12.55.57.png

Install ethers, and a very specific version:

npm install ethers@5.7.2

Why the specific version? Because in the latest version of ethers, they changed fundamental shit under the hood (how you instantiate the signer and provider I believe).

At some point I’ll need to dive into that, but for now, this is just easier.

Front end

This is what the front end looks like:

Screenshot 2023-07-05 at 13.53.16.png

I didn’t include any styling (except some inline border colours), so you can play around with it however you want. Maybe later I’ll use Chakra later to spice it up.

This is what my code looks like in the page.js file inside the app folder

'use client'
import { useState } from 'react';
import { ethers } from 'ethers';
import { abi } from './abi';

export default function Home() {
  const [favoriteNumber, setFavoriteNumber] = useState('');
  const [storedNumber, setStoredNumber] = useState('');
  const [personName, setPersonName] = useState('');
  const [personFavoriteNumber, setPersonFavoriteNumber] = useState('');
  const [mappedFavoriteNumber, setMappedFavoriteNumber] = useState('');

  const contractAddress = '0x5544cbe52b0797f71B4186469a0797Bdb260abf6';

  // Connect to the contract
  async function connectToContract() {
    if (window.ethereum) {
      await window.ethereum.enable();
			//connect to blockchain via metamask
      const provider = new ethers.providers.Web3Provider(window.ethereum);
			//connect your wallet via metamask
      const signer = provider.getSigner();
      const contract = new ethers.Contract(contractAddress, abi, signer);
      return contract;
    } else {
      console.error('Please install MetaMask to interact with the contract.');
    }
  }

  // Store favorite number
  async function storeFavoriteNumber() {
    const contract = await connectToContract();
    if (contract) {
      const tx = await contract.store(favoriteNumber);
      await tx.wait();
      setFavoriteNumber('');
      retrieveFavoriteNumber();
    }
  }

  // Retrieve favorite number
  async function retrieveFavoriteNumber() {
    const contract = await connectToContract();
    if (contract) {
      console.log("contract OK")
      console.log(abi)
      const number = await contract.retrieve();
      setStoredNumber(number.toString());
    }
  }
 

   // Retrieve favorite number of a person
  async function retrievePersonFavoriteNumber(personName) {
    const contract = await connectToContract();
    console.log(personName)
    if (contract) {
      const number = await contract.nameToFavoriteNumber(personName);
      setMappedFavoriteNumber(number.toString());
    }
  }
  
  
  // Add a person
  async function addPerson() {
    const contract = await connectToContract();
    if (contract) {
      const tx = await contract.addPerson(personName, personFavoriteNumber);
      await tx.wait();
      setPersonName('');
      setPersonFavoriteNumber('');
      retrievePersonFavoriteNumber(personName);
    }
  }

  return (
    <>
      <div>
        <h1>SimpleStorage Contract Interaction</h1>
        <div>
          <h2>Store Favorite Number</h2>
          <input
            type="number"
            value={favoriteNumber}
            onChange={(e) => setFavoriteNumber(e.target.value)}
            style={{ border: '1px solid black' }}
          />
          <button onClick={storeFavoriteNumber} style={{ border: '1px solid black' }}>Store</button>
        </div>
        <div>
          <h2>Retrieve Favorite Number</h2>
          <button onClick={retrieveFavoriteNumber} style={{ border: '1px solid black' }}>Retrieve</button>
          <p>Stored Number: {storedNumber}</p>
        </div>
        <div>
          <h2>Add Person</h2>
          <input
            type="text"
            value={personName}
            onChange={(e) => setPersonName(e.target.value)}
            placeholder="Name"
            style={{ border: '1px solid black' }}
          />
          <input
            type="number"
            value={personFavoriteNumber}
            onChange={(e) => setPersonFavoriteNumber(e.target.value)}
            placeholder="Favorite Number"
            style={{ border: '1px solid black' }}
          />
          <button onClick={addPerson} style={{ border: '1px solid black' }}>Add</button>
        </div>
        <div>
          <h2>Retrieve Favorite Number of a Person</h2>
          <input
            type="text"
            value={personName}
            onChange={(e) => setPersonName(e.target.value)}
            placeholder="Name"
            style={{ border: '1px solid black' }}
          />
          <button onClick={() => retrievePersonFavoriteNumber(personName)} style={{ border: '1px solid black' }}>
            Retrieve
          </button>
          <p>Mapped Favorite Number: {mappedFavoriteNumber}</p>
        </div>
      </div>
      </>
  );
}

Make sure to replace the contract address with the actual address of your SimpleStorage contract.

Create a new file called abi.js in the root folder of your Next.js project with the following code:

export const abi = [
  // Paste the ABI of the SimpleStorage contract here
];

Replace // Paste the ABI of the SimpleStorage contract here with the actual ABI of your SimpleStorage contract. Make sure to export the abi variable using export const to make it accessible to other files.

To run the Next.js project, use the following command:

npm run dev

Open your browser and visit http://localhost:3000 to see the contract interaction page.