Creating a storage factory smart contract in Solidity and interacting with it (notes from Freecodecamp)
Tags: web3, solidity, smartcontract, blockchain
Published: 2022-08-13 22:26:00

I'm following Freecodecamp's wonderful web3 course on Youtube (link here => https://www.youtube.com/watch?v=gyMwXuJrbJQ&ab_channel=freeCodeCamp.org), and to make sure that I remember what I've learnt, I like to take notes. Here, I'll post my notes from `Lesson 3: Remix Storage Factory`. I'll make my disorganized notes human readable so it will look like a tutorial.

Please note that all the credit goes to Patrick Collins and incredible Freecodecamp team/community, I am just delivering what I've seen in written format so that I won't have to rewatch the tutorial in the future. I hope you too can benefit from it.

As this is the 3rd lesson, there are some fundamentals missing here. It also takes the SimpleStorage smart contract introduced to us in Lesson 2 of the Freecodecamp course. You might want to check it out before reading this post, although I'll try to explain what's going on in the SimpleStorage contract. If I'm mistaken at any point, please please feel free to correct as it's been more than a week  since I've watched the Lesson 2, so I might have forgotten some of the reasons why we're doing things as we do :)

We will use Remix IDE for this post, so make sure you go to `https://remix.ethereum.org/` and be ready for hacking!

Note: You can find the code for all the contracts here in Patrick's GitHub repo, here => https://github.com/PatrickAlphaC/storage-factory-fcc

## Simple storage smart contract

Let's start by looking at the SimpleStorage smart contract .

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {

    uint256 favoriteNumber;

    struct People {
        uint256 favoriteNumber;
        string name;
    }
    // uint256[] public anArray;
    //create an array of type People, which is a struct and name it people. It is also public.
    People[] public people;

    mapping(string => uint256) public nameToFavoriteNumber;

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

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

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

All Solidity smart contracts start with a SPDX-License-Identifier. This is a license that the smart contract is under. In this case, it is MIT. If I'm not mistaken, that means it's open source.

Then, we have to define the Solidity compiler version. ` ^0.8.0` means "anything above 0.8.0" is okay.

Then, we use the `contract` keyword to let the compiler know that we're creating a smart contract. Our contract is named `SimpleStorage`.

Since Solidity is a typed language, we need to define the type of the variable we're creating. I'm a Javascript developer normally, so my mind works in terms of JS. This is how I see it: Instead of saying `let favoriteNumber` or `const favoriteNumber`, we say `uint256 favoriteNumber`. If we were to define a string, we'd have to say `string favoriteNumber`.

Now we have our `favoriteNumber` variable ready, we're creating a struct of People. Structs are like objects in Javascript. They take key/value pairs. In this case, they're `uint256 favoriteNumber` and `string name`.

Then, we create a dymamic array named `people` with the type People struct. I don't know Typescript or any other typed language other than Solidity yet, so this part was (and still is) quite confusing for me. It's like, this `people` array can only take People structs as values. It cannot take a single string or an array of strings or uints or anything other than People structs. It can't take other type of structs also if I'm not mistaken; it can only and only take People structs as values and that's all.

Also, with this syntax `People[] public people`, we're telling the compiler that this is a `dynamic array` of People structs. That is to say, the length of the array is not defined-because you can do that in Solidity, i.e. you can have arrays with predefined length.

On more thing about the array, you'd notice the `public` keyword here. It means that this variable is public, i.e. it can be seen and called outside of the contract. If we'd say `private` instead of it, you couldn't access it from outside of the contract.

Then we have a mapping of type string to uint256. Mappings too confused me a lot in Solidity. They're like objects in JS, but instead of taking multiple values like structs, they take only one key/value pair and they work similar to arrays in Javascript. They come in quite handy though, especially when mapping addresses.

Our first function, `store` is a public function that takes a single parameter `_favoriteNumber` of type `uint256` and changes the `favoriteNumber` variable to the value of `_favoriteNumber` parameter. The underscore (`_`) is just a convention in Solidity, it's for parameters.

Then, we have a function `retrieve` that is public and view. This means that it can be seen and called from outside of the contract. It returns a `uint256` value and it DOES NOT cost gas. That's because it doesn't change the state of EVM (Ethereum Virtual Machine). Snce there's no change, there's no gas cost.

Then, we have a function `addPerson` that is public. It takes two parameters `_name` and `_favoriteNumber` of type `string` and `uint256`. Now, the first line inside this function, ` people.push(People(_favoriteNumber, _name));`, does the following: it takes the parameters and creates a People struct with them and pushes this new People struct into the people array.

The second line, ` nameToFavoriteNumber[_name] = _favoriteNumber;`, does the following: it takes the parameters and creates a key/value pair with them and puts this new key/value pair into the nameToFavoriteNumber mapping. You see, in the mapping ` mapping(string => uint256) public nameToFavoriteNumber;` we have a string and uint256, so the `_name` goes as the string, and `_favoriteNumber` goes as the uint256.

That's all for the SimpleStorage.sol contract. You can paste this contract to remix, deploy and play with it. You'll notice that so far we can only retrieve the favoriteNumber as we only created a getter function for that.

## StorageFactory smart contract

For this part, we'll create a new contract in Remix IDE, named `StorageFactory.sol`. The idea in this one is to create a new contract that can create other smart contracts. Yes, smart contracts can do that. "The ability for contractsto seamlessly interact with each other is known as `composability`. "

Let's check out the final version of StorageFactory smart contract:

```solidity

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./SimpleStorage.sol";

contract StorageFactory {

    SimpleStorage[] public simpleStorageArray;

    function createSimpleStorageContract() public {
        SimpleStorage simpleStorage = new SimpleStorage();
        simpleStorageArray.push(simpleStorage);
    }

    function sfStore(uint256 _simpleStorageIndex, uint256 _simpleStorageNumber) public {

        simpleStorageArray[_simpleStorageIndex].store(_simpleStorageNumber);
    }

    function sfGet(uint256 _simpleStorageIndex) public view returns (uint256) {

        return simpleStorageArray[_simpleStorageIndex].retrieve();
    }
}
```

As you can see, after our `pragma solidity` line, we have this import statement => `import "./SimpleStorage.sol";`. Importing works like Javascript. We can either copy paste the code, or import it like this to make it much more manageable.

Now, with ` SimpleStorage[] public simpleStorageArray;` we create a public array named simpleStorage (minuscule) with the type of SimpleStorage (majuscule) contract that we've just imported. So, this array will contain only and only SimpleStorage contracts. Cool, right?

The public function `createSimpleStorageContract` does two things: In the first line, it creates a variable called `simpleStorage` (minuscule) with the type of `SimpleStorage` (majuscule) contract. It does this with the `new` keyword. When Solidity sees the 'new' keyword here in this context, it says "aha! We're going to deploy a new SimpleStorage contract." In the second line, it pushes this new contract into the `simpleStorageArray` array.

The function `sfStore` ("sf" stands for "storageFactory") takes two uin256 parameters: the index of the contract just created and pushed into the array, and the favorite number that was in the `simpleStorage` contract.

Remember, the `store` function that stored the favorite number in `simpleStorage.sol` was as such:

```solidity
function store(uint256 _favoriteNumber) public{
favoriteNumber = _favoriteNumber;
}
```

Then, with the line ` simpleStorageArray[_simpleStorageIndex].store(_simpleStorageNumber);` it stores the favorite number given in `uin256 _simpleStorageNumber` parameter to the `simpleStorageArray` at the index given in the parameter `_simpleStorageIndex`. It does so by calling the `store` function in `simpleStorage.sol` that I've shown above.

I know it sounds complex, and maybe it is, it's just, we're writing a function so that we can choose whatever `SimpleStorage` we've created using the `createSimpleStorageContract` function using its index in the array so that we assign it a favorite number.

The next and the last function in this contract, `sfGet` ("sf" standing for "storageFactory" again) is a public getter function and we know that it does not cost us any gas because it contains the `view` keyword in it. It takes the index of the simpleStorage contract we've created via `createSimpleStorageContract` function and returns the favorite number that was in that contract by callng the `retrieve` function in `simpleStorage.sol` contract. That `retrieve` function was as such:

```solidity
//retrieve function in simpleStorage.sol
    function retrieve() public view returns (uint256){
        return favoriteNumber;
    }

    
```

Now say, if I opened the Remix IDE, compiled and deployed the `StorageFactory.sol` contract, and then I called the `sfGet` function, create a bunch of contracts using the `createSimpleStorageContract` function, and say, called the `sfStore` function with the parameters `0,22` and then called the `sfGet` function with the parameter `0`, I would get `22` as the favorite number. If I called the `sfStore` function with the parameters `2,378` and then subsequently call the `sfGet` function with the parameter `2`, I would get `378` as the favorite number.

That's it. Now, we have one more thing to learn in this post, inheritance.

## Inheritance

In the tutorial, Patrick shows us how we can create a copy of a contract and even overriding it in this or that way. For that, we need to create a new contract called `ExtraStorage.sol`. Now, if we import `SimpleStorage.sol` contract in this new `ExtraStorage.sol` contrat and define it as `contract ExtraStorage is SimpleStorage ` instead of just declaring `contract ExtraStorage` as we'd normally do, this new `ExtraStorage` contract will have all the logic the variables, the functions and all in `SimpleStorage` contract.

But he goes even further. What if we wanted to change some things in our copy of `SimpleStorage` (which is our `ExtraStorage` contract) and we wanted to add some more functionality to it?

Then we'd need to `override` it. Check this snippet of `ExtraStorage.sol` out:

```solidity
// SPDX-License-Identifier: MIT

pragma solidity 0.8.8;

import "./SimpleStorage.sol";

contract ExtraStorage is SimpleStorage {
    function store(uint256 _favoriteNumber) public override {
        favoriteNumber = _favoriteNumber + 5;
    }
}

```

You see, we're overriding the `store` function in `SimpleStorage.sol` contract. This is done by the `override` keyword. We're just adding the number 5 to everyone's favorite number for heuristic purposes.

But this can't work by itself. It wouldn't override like so. We need one more extra step so that it would work seamlessly. We need to go back to our original `SimpleStorage.sol` contract and add the `virtual` keyword to the function we want to override later on. In this case, it is the `store` function in `SimpleStorage.sol`.

So, we open the `SimpleStorage.sol` contract, and change the `store` function to this:

```solidity
    function store(uint256 _favoriteNumber) public virtual {
        favoriteNumber = _favoriteNumber;
    }

```

Now, this `store` function is "overrideable". We can create a copy of `SimpleStorage.sol` and override its `store` function as we please.

That's all folks! I hope you enjoyed this post. I hope to continue publishing my notes if everything goes according to the plan. Yet I highly suggest that you check the original tutorial on Freecodecamp's Youtube channel, it is phenomenal.

Keep calm & happy coding!

Edit: You can find the notes for Lesson 4: Remix Fund Me here => https://dev.to/muratcanyuksel/creating-a-fundme-smart-contract-in-remix-ide-notes-from-freecodecamp-3imm