Data Location Assignment [OLD]

I did my research and I found two solutions, for me a clearer way to do this exercise was to update the balance directly in the mapping, then I found the other solution to change the data location from memory to storage.

// this function takes uint id & uint balance as arguments and updates balance
    function updateBalance(uint id, uint balance) public {
        // 1st solution: we can update balance by changing it directly in the mapping 
        users[id].balance = balance;
      // 2nd solution: When we have data location "memory"  the balance is not permanently stored and won't be updated.
      // we need to change data location to "storage" to be able to update any changes in User struct 
       User storage user = users[id];
       user.balance = balance;
      
    }
1 Like

pragma solidity 0.5.1;
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

Hi,

I was just wondering when we see any withdraw function which is onlyOwner, does that may mean is an exit scam scmart-contract? if not, besides for secutiry reasons onlyOwner and not a “normal” user is intended to withdraw the funds, is it normal this function is written in a DeFi token?

Just wondering, I know you have Secutiry Contract. course but haven’t get there yet.

Thank you in advance for your answers.

1 Like

Solution 1:
function updateBalance(uint id, uint balance) public {
// directly set balance in the mapping
users[id].balance = balance;
}
Solution 2:
function updateBalance(uint id, uint balance) public {
// change the user variable to storage type, will then update users mapping
User storage user = users[id];
user.balance = balance;
}

1 Like

amended the updateBalance function to:

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

Yes

This causes a specific user’s balance to be updated in the mapping. The “pointer” isn’t something that the mapping “has”. It is a reference to a particular place in the mapping, from somewhere else in the program.

users[id]  is the “standard” way to “point to” a specific User instance (a user’s record) in the users mapping, because the ID is the key to that specific User instance. [id] references the function’s id parameter which is input by the function caller, and represents the user whose balance we want to update.
So, probably the most obvious (and easiest to understand) way to update a user’s balance is as follows:

users[id].balance = balance;

balance to the right of the assignment operator (=) references the function’s balance parameter which is input by the function caller, and represents the new balance.
This new balance is assigned to  users[id].balance  which is effectively a pointer to where the new balance should be assigned. As explained above users[id] points to the “record” of a specific user in the users  mapping, and by adding the balance property, it now points to the specific value held in that property which represents the user’s existing balance. As a result, the user’s balance stored in the mapping is updated.

With   User memory user = users[id]   the pointer itself is assigned to a local user variable, which means that a copy is made, and stored locally in memory, of the specific user’s record which the pointer references in the mapping. After being assigned, the pointer users[id] is no longer used within the function. So instead of the mapping being updated with the new balance,  user.balance = balance  updates the local copy with the new balance. This update is lost when the function is exited, because the change has not been stored permanently in the mapping, but instead only temporarily in memory.

With   User storage user = users[id]   the pointer is assigned to the variable user, and because the data of both user and the mapping users is located in storage, this effectively creates a copy of the pointer, and means that within the function both users[id] and user will act as identical pointers to the same user’s record in the mapping. This is why, in their own specific context, the following lines of code both update a specific user’s balance in the mapping:

users[id].balance = balance;
user.balance = balance;

Sorry, about the long answer, but it’s very hard to answer your question without explaining the details of what’s actually happening. Anyway, I hope this helps to make it a bit clearer. Understanding these types of concepts is often a gradual process, so don’t be surprised if you still don’t immediately understand this fully. With more study and practice, you’ll then probably find that later on things will suddenly fall into place and click :slight_smile:

1 Like

Nice solution @alexsei :ok_hand:

Just to clarify…

…the balance property is not updated in the actual struct, but in a specific instance of that struct within the mapping. I’m sure this is what you have understood, but I just wanted to clarify it.

1 Like

A nice analysis of two alternative solutions @MrAndres :ok_hand:

Just to clarify…

…the balance is not updated in the actual struct, but in a specific instance of that struct within the mapping. I’m sure this is what you have understood, but I just wanted to clarify it.

2 Likes

pragma solidity 0.5.1;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

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

//The "user" object is in memory so it will be lost after execution. In order to updated the balance in storage, we should updated the user in "users" array.
function updateBalance(uint id, uint balance) public {
     User memory user = users[id];
     user.balance = balance;
     users[id] = user;
}

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

}

1 Like

pragma solidity 0.5.1;
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 memory user = users[id];
     user.balance = balance;
     users[id] = user;
    
}

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

second solution
pragma solidity 0.5.1;
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;
}

}
I prefer the first one to have memory storage for the user instead storing it permanently.

1 Like

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

1 Like

pragma solidity 0.5.1;
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 memory user = users[id];
     users[id].balance = balance;
}

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

}

1 Like

Change memory to storage.

1 Like

pragma solidity 0.5.1;
contract MemoryAndStorage {

mapping(uint => User) users;

struct User{
    uint id;
    uint balance;
}

addUser(updateBalance);
addUser.push (Users[id]);

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

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

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

}

I am not entirely sure if i have written the code exact, but my understanding is that there isn’t a .push function to save to the storage to enable updating?

I loved trying to work it out tho haha…

lol ok, clearly over thought this

1 Like

Change “memory” to “storage” so the updated balance is stored in the mapping and not forgotten once the function has been executed.

function updateBalance( uint id, uint balance ) public {

User storage user = users[id];

User.balance = balance;

}

1 Like

Hi @NetworkP,

Yes…your solution attempt is certainly creative! But, unfortunately, it’s too freestyle to work :wink:

You can’t pass a function to addUser because its parameters are defined as unsigned integer data types.

.push() is an array method, so you can only use it to push values to an array. I’m not really sure what you’re trying to push to here. You can’t push to a function, or a mapping, and the mapping has the name users, anyway, not Users.

Having watched the solution video, do you now understand how the problem is solved? I would encourage you to have a look through a selection of other posts in this discussion thread. There are several different alternative solutions, and you’ll find them all here, together with feedback, corrections, discussions and explanations :slight_smile:

Basically, we need to ensure that the updateBalance function body contains code that updates the balance stored in the mapping (permanent storage) with the new balance. We find a specific user instance in the mapping with the key (ID) using users[id]. Then we assign the new balance to that user instance’s balance property. I think this is most clearly shown with the following one line solution:

users[id].balance = balance;
/* .balance to the left of the assignment operator (=) references the
    specific user's balance property */
/*  balance to the right of the assignment operator references the new balance 
    value passed into the function (as an argument/parameter) when called.  

I’m very pleased to hear that, as this is very important! :smiley:

1 Like

Hi @mburtonshaw,

Your solution is nearly correct. You just need to make sure that you are assigning the new balance to the user (not User) variable. User defines the data type, user is the name of the variable.

Hello Burton. Thanks. I understood why balance wouldn’t update but rough around the edges implementing it haha…i find coding one of my weaker points of the whole course, but i enjoy it and so determined to make great progress before the end of 2020.

Thanks for your clarification, and yes, after going through the solution it is more clear in terms of understanding the code.

1 Like

:muscle:

If you carefully analyse the solutions and work out how they do what they do, then you’ll gradually find that you are able to come up with them yourself :slight_smile: Keep on persevering!

By the way I’m Jon, not Burton, but you can call me Burton if you like :joy:

1 Like