Data Location Assignment

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;
}

}

Nice solution @Bensen :ok_hand:

:raised_hands:

Good point :+1:

In fact, even though the initial idea of this assignment is to update the existing balance by replacing it with the new one, I actually think that it makes more sense to add an amount to the user’s existing balance instead. So, I would still just have the one updateBalance() function — changing its name to addFunds() if you want — and replace the assignment operator=  in the second line of code …

… with an addition assignment operator+=

user.balance += balance;

In addition, if we add to, instead of replacing, the existing balance, I think the code will look clearer and more readable if we also change the name of the balance parameter to amount , because we are adding an amount to the balance, rather than replacing it with a new balance

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

There is always more than one solution to these assignments, and alternative interpretations are equally valid and to be encouraged. You can always add comments to your code to explain anything you’ve done a bit differently or are unsure about :slight_smile:

Just let me know if you have any questions.

Nice alternative solutions, @LELUK911 :ok_hand:

Even though you probably realised that the initial idea of this assignment is to update the existing balance by replacing it with the new one (your main solution), I actually think that it makes more sense to do what you’ve shown in your in-line comment as an alternative, and add an amount to the user’s existing balance using the addition assignment operator+= (instead of the assignment operator= ).

However, if we add to, instead of replacing, the existing balance, I think the code will look clearer and more readable if we also change the name of the balance parameter to amount , because we are adding an amount to the balance (to give a new total balance), rather than replacing it with the new total balance.

Just let me know if you have any questions :slightly_smiling_face:

1 Like

Hi @imran82,

Your solution won’t compile and you should have got a red compiler error for this line …

This is because you are trying to assign the new balance to the balance property of an undefined user variable. You either need to first define user in a line of code above, or directly reference the User instance (the user’s record) in the mapping and assign the new balance to that.

Have a go at correcting this. Let me know if you have any questions, or if you need any more help :slight_smile:

1 Like

i am not able to compile because new remix version doesnt let me to compile with older version

Try activating Auto Compile (mark the Auto Compile box under Compiler Configuration in the Solidity Compiler panel in Remix).

Remix should then automatically select the correct compiler version (0.7.5) in the Compiler field at the top of the Solidity Compiler panel. If it doesn’t, then you can manually select it from the dropdown.

If, for some reason, you only have one compiler version available to you in this dropdown (e.g. the latest, 0.8.11) then just change your contract’s pragma statement to this same version.

You need to be able to compile your contracts, otherwise you won’t identify any errors. You also won’t be able to deploy your contracts if they haven’t been compiled.

If you can only compile with the latest version (v0.8), you can still do the course with this version instead of v.0.7.5 (used in the videos). There is only one syntax change in v.0.8 that affects this course, which I can explain to you if necessary. Just let me know.

Thanks for help. Now remix id is working properly. It was giving error as i was using struct instead of mapping. Now i have updated it
function updateBalance(uint id, uint balance) public {
users[id].balance = balance;
}

Thanks for pointing it out. I hope you will help me out in future also.

2 Likes

so my original explanation was wrong. :sweat_smile:
but my code was right so yay?!

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;
    }

}
2 Likes
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;

    }

}
2 Likes

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];  // use storage instead of memory for referen to location of storage user 
     user.balance = balance;
}

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

}

2 Likes

In the updateBalance function, store the User data in storage instead of memory. Data stored in memory will not persist after their function scope finishes. Data stored in storage will persist after the function finishes executing.

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 @imran82,

Glad you resolved this in the end :slightly_smiling_face:

Nice solution @DeMarco_Saunders :ok_hand:

… and welcome to the forum! I hope you’ve been enjoying the course :slightly_smiling_face:

What was your original explanation? … and do you now understand how the solution you’ve chosen (with the local storage variable) works? You’ll find a full explanation in this post.

Just let me know if you have any questions.

Nice solution @fooku :ok_hand:

… and welcome to the forum! I hope you’ve been enjoying the course :slight_smile:

You definitely have the right idea here :+1:

When we define the data location of a local variable as storage , this creates a storage pointer . This references the value already saved in persistent storage which is assigned to it: in our case users[id]

A storage pointer is like a temporary “bridge” during execution of the function to wherever in the contract state it points to (in our case a specific User struct instance in the mapping).

Any value which is then assigned to a property of the local “pointer” (user) will effectively update that same property of the specific User instance in the mapping which is referenced by the pointer. This enables a specific user’s balance (stored persistently in the mapping) to be reassigned with the new balance (input into the function), before our “bridge” disappears when the function finishes executing…

user.balance = balance;

Just let me know if you have any questions.

Nice solution @MichaelAudire :ok_hand:

… and on the whole, your explanation is good. Here are some additional comments …

Yes … it was a temporary copy of the whole User struct instance (stored in the mapping) which was initially stored in memory …

User memory user = users[id];

Then, the new balance was assigned to the balance property of this temporary copy, where it was only saved until the function had finished executing, at which point it was lost …

user.balance = balance;

When we define the data location of a local variable as storage, this creates a storage pointer. This only points to (references) the value already saved in persistent storage which is assigned to it (in our case users[id] ); it doesn’t create or store a separate copy of this value, as it would if we defined the data location as memory. A storage pointer is like a temporary “bridge” during execution of the function to wherever in the contract state it points to (in our case a specific User struct instance in the mapping).

Any value which is assigned to a property of the local “pointer” (user) will effectively update that same property of the specific User instance in the mapping which is referenced by the pointer. This enables a specific user’s balance (stored persistently in the mapping) to be reassigned with the new balance (input into the function), before our “bridge” disappears when the function finishes executing.

user.balance = balance;

And because …

… the user’s new balance will also be stored persistently in the mapping, until it is updated again.

Just let me know if you have any questions :slight_smile:

1 Like

Here i added a require statement to check that the balance input is not 0, then i set the value of the balance in the User struct to the input balance.

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 {

         require(balance != 0, "Add a vild amount");

         //User storage user = users[id];

         //user.balance = balance;

         users[id].balance = balance;

    }

    function getBalance(uint id) view public returns (uint) {

        return users[id].balance;

    }

}
2 Likes

Nice alternative solutions, @Arch.xyz :ok_hand:

… and welcome to the forum! I hope you’re enjoying the course :slight_smile:

Your require() statement is correctly coded, but why can’t a user’s balance be updated to zero?

Yes i am, and im really looking forward to starting the second course :sunglasses:

1 Like

Hi everyone, very happy to be joining the forum. I think the easiest way would be to have the user in the updateBalance function, be stored in storage.

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.8.7;

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