Additional Solidity Concepts - Discussion

my code.

pragma solidity 0.7.5;
contract MemoryAndStorage {

    mapping(uint => User) users;

    struct User{
        uint id;
        uint balance;
    }

    function addUser(uint id, uint balance) public {
        users[id] = User(id, balance);
    }

    function updateBalance(uint id, uint balance) public {
         users[id].balance = balance;
    }

    function getBalance(uint id) view public returns (uint) {
        return users[id].balance;
    }

}
1 Like

Andrew, could it be because you push newWord which is stored in memory?
Have you tried defining a state variable: stringToAdd in storage and then pushing that?
Best, Raphael

Hi @raphbaph,

There isn’t actually a problem with @andrewfleming’s code, as you can see from the reply he got…

If you deploy his code yourself, you will see that it works perfectly.

You are right that the parameter newWord is stored temporarily in memory, but it is then pushed directly to the array which is saved in persistent storage. This is effectively carrying out the same type of assignment as your solution to the Data Location Assignment…

… where the temporarily-stored balance is directly assigned to the users mapping in persistent storage.

State variables (which include arrays), mappings and structs are saved in persistent storage by default, and therefore do not require us to explicitly define their data location. However, as with strings and struct instances, we do need to do this with arrays when they are assigned to a local variable.

If you mean a variable within the addToPoem function, then this wouldn’t be a state variable. All variables defined within functions are local, and are therefore lost when the function finishes executing. Do you mean we could define a local variable with the keyword storage in order to create a pointer to a value saved in persistent storge? As far as I’m aware, this is only a useful alternative to direct assignment with struct instances, like we did with the alternative solution in the Data Location Assignment…

User storage user = users[id];  /* This is a pointer to a specific
                                   struct instance saved in a mapping */ 
user.balance = balance;

Have you tried implementing your suggestion to see if it works? If so, it would be interesting to see the code to get a better idea of your thinking.

It’s great that you are thinking about how to apply what you’ve learnt about data location to other scenarios. This post is meant to help you with that, and certainly not to discourage you from participating in these discussion topics. Some of these data location concepts are complex, and take a while to fully understand and master, but getting actively involved in forum discussions with certainly help with that.

Let me know if anything I’ve explained is unclear, or if you have any questions :slight_smile:

1 Like

Hey Jon,
Thanks a lot!
I read the replies too late and yes @andrewfleming’s code works fine.

I defined the state variable addToPoem globaly and reused it.
That worked as well.
Unfortunately I didn’t save the code… too bad…

However I get the concept better now.
When I set the state variable to the local variable, the state of the local variable now becomes permanent.
This is very similar to how Go does it.

Thinking about scope is really tricky to me and takes some time, but it’s definitely fascinating.

1 Like

Hi @raphbaph,

OK, I thought perhaps that might be what you meant. So, do you mean like this?

pragma solidity 0.6.3;

contract DexquisiteCorpse {
   
   string addToPoem;
   string [] public etherPoem = ["let "];

   modifier costs(uint cost) {
       require(msg.value >= cost);
       _;
   }

   function addToPoem(string memory newWord) public payable costs(1 ether) {
       addToPoem = newWord;
       etherPoem.push(addToPoem);
   }
}

This works, but it’s very wasteful in terms of gas, because you are defining an additional state variable. Storage is costly, and as you now know, we don’t need the additional state variable because we can just push newWord to the array directly.

Yes, I can see you are starting to understand what’s happening now :slight_smile: but a local memory variable remains temporary and is lost when the function finishes executing. However by referencing it and assigning (or pushing) its value to a state variable, we don’t lose the value that was stored in the local variable.

In @andrewfleming’s code, we don’t even use a local variable, we just assign/push the temporarily-stored parameter’s string value directly to the persistently-stored array in storage. The parameter itself is still lost when the function finishes executing.

With a local storage variable, a pointer is created. The pointer itself is still only temporary, but it acts as a kind of temporary “bridge” (while the function is executing) to the value stored in the contract state, which it points to. Therefore, any value that we assign to this pointer’s identifier (i.e its name) is automatically assigned to wherever it points to in persistent storage…

Yes… it does take time, and a very inquisitive mind, to get to grips with all of the complexities of scope … but you will get there :muscle:

I hope that clarifies things even more, rather than confusing you! :crazy_face:

1 Like

Totally does :slight_smile:

I rewrote it to something similar to @andrewfleming later. Which is also how @filip did it in the vid.

1 Like

data location assignment

changed

User memory user = users[id];
to
User storage user = users[id];

1 Like

Don’t store the balance in a local variable where it gets wiped, but put it into persistant data
remove:
//User memory user = users[id];
//user.balance = balance;
add:
users[id].balance = balance;

1 Like

Hi guys… very new to programming and apologies for my ignorance. I don’t understand why in the function below:

function addUser(uint id, uint balance) public {
users[id] = User(id, balance);
}

users[id] takes on two parameters and not one. also please correct me if i’m wrong here, but is [id] a newly introduced variable, and not from

struct User{
uint id;
uint balance;
}

Getting confused…

2 Likes

Hey @Yutaro_Shimizu, hope you are ok.

You are forgetting about the array, which is users (which is based on the struct).

In the addUser function, It adds the id and balance values based on the User struct to the [id] on the users array.

Hope i expleined myself good, if not let us know :nerd_face:

Carlos Z

1 Like

Topic: Events
Not sure if this is the correct place to post this, but I’m sure peoples will let me know;

I have tried to add an emit function for transferred as Filip advised us to try,

image

However, for some reason I am getting the below error code in the console,

image

Can anyone advise?

Hi @Andre_Mark,

Your transfer event declaration is correctly coded. However, notice that the transfer() function executes a transaction that involves 2 user addresses (the sender and the recipient). This is different to the addBalance function, which executes a transaction that only involves 1 user address (the depositer). So, in the transfer event it would be useful to include the sender’s address, as well as the recipient’s address.

There are a couple of issues with your corresponding emit statement:

  • You have declared your second parameter in the transfer event with the name transferredTo, but in the emit statement the second argument is referencing msg.sender, which is the address the transfer is being made from (not to).
  • The event should only be emitted once the event itself has been executed, because the logged event should act as confirmation that the event has ocurred. Therefore, we usually place an emit statement at the end of the function which emits the event (but before any return statement).

The error message appears to be telling you that you’ve called the transfer() function with only one argument. It expects the first argument to be an address, but it looks like you’ve input an integer of 500. That’s why it’s telling you that the address is invalid.
The transfer() function has 2 parameters: an address (for the recipient of the transfer), and an unsigned integer (the amount to be transferred). Therefore, in the transfer function’s input box in Remix, you need to:

  1. input an address: choose one from the Account field dropdown, copy it to the clipboard, and then paste it into the input box;
  2. add a comma (arguments need to be separated from each other by a comma);
  3. input an amount;

Alternatively you can click on the down arrow next to the input box and you’ll get a separate input box for each argument. If you use these you don’t need to add the separating comma.

Post your corrected code in the Events Assignment discussion topic, and I’ll take a look at it :slight_smile:

Just let me know if anything is unclear, or if you have any questions.

@filip Is there a tool or a way to view the state storage content of a specific smart contract? (with no code)

E.g., I want to see all the mapping content of balances in a specific token without using APIs or functions?

And where is the storage of these state storage? is it in the blocks? Can you guide me here to understand the internals of how the node store the state and the location of the storage?

Thanks

Hey @mohkanaan, hope you are well.

It must be through a dapp (code require), here is an example of a dapp that tracks the DAI Stats (the DAI contract stats) https://daistats.com/#/

Impossible, unless the contract store publicly all the addresses that have a balance with the contract, remember that mappings are key to value storage, meaning that you need to know the key to get the value of it, if the contract does not have a public function to get all the addresses, is impossible to know how many keys contains a value on the mapping.

This will require an extensive learning on the nodes, how do they work and how the data is storage on files that we call “blocks”. You might have to research it by yourselve :face_with_monocle:

If you are looking to build a dapp that can have features like tracking the balance of an address on multiple protocols, you might wanna take a look at https://moralis.io/ (JS programming require).

Carlos Z

1 Like

Thanks @thecil for the answers, but I still need to know the mechanism of how the key-value state storage is stored in the blockchain; unless there is encryption and we don’t know the decryption key, there must be a way to view the content of the storage without using smart contracts/solidity code (I mean here using a code connected to the blockchain directly and not through EVM)

pragma solidity 0.7.5;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

function addUser(uint id, uint balance) public {
    users[id] = User(id, balance);
}

function updateBalance(uint id, uint balance) public {
     User **storage** user = users[id];
     user.balance = balance;
}

function getBalance(uint id) view public returns (uint) {
    return users[id].balance;
}

}

1 Like

pragma solidity 0.7.5;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

function addUser(uint id, uint balance) public {
    users[id] = User(id, balance);
}

function updateBalance(uint id, uint balance) public {
     User storage user = users[id];
     user.balance = balance;
}

function getBalance(uint id) view public returns (uint) {
    return users[id].balance;
}

}

1 Like

Hey filip,

In the update balance assignment when I deploy the contract and interact with it it actually never updates the balance? I first add a user and balance then updateBalance (change the balance) get balance but still show me the previous balance? not sure what I’m doing wrong…

function updateBalance(uint id, uint balance) public {
User storage user = users[id];
user.balance = balance;

1 Like

Hey @berto, hope you are ok.

Would be great if you can provide your entire contract in the following way so i can review it :face_with_monocle:

Carlos Z

Hey Carlos, thanks for your reply please find code bellow and screenshot thanks!

pragma solidity 0.7.5;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

function addUser(uint id, uint balance) public {
    users[id] = User(id, balance);
}

function updateBalance(uint id, uint balance) public {
     User storage user = users[id];
     user.balance = balance;
}

function getBalance(uint id) view public returns (uint) {
    return users[id].balance;
}

}

remix

1 Like