Data Location Assignment [OLD]

Well, I fixed it. But how I did it… yeah… i can’t really explain.
I know I had to remove “Memory” because we don’t want de the contract to forget the new balance after the function was called. And then i just rewrote the code in a way that seemed logic considering the other code that was used in the contract. Maybe someone else can enlighten me why my code fixed the problem. (or how it can be written in a better way)

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 {
     users[id] = User(id, balance);
     users[id].balance = balance;
}

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

}

}

1 Like

pragma solidity 0.5.1;
contract MemoryAndStorage {
struct User{
uint id;
uint balance;
}

mapping(uint => User) users;

function addUser(uint inId, uint inBalance) public {
    User memory theUser;
    theUser.id = inId;
    theUser.balance = inBalance;
    users[inId] = theUser;
}

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

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

}

1 Like

The updateBalance function does not actually update the balance of the user permanently, only temporarily within the function. This is because the user data is defined with “memory”, and is only saved during the function execution. In order to fix this, “memory” must be removed from the function. It can either be replaced with “storage” or simply have no data storage definition, because the mapping is already saved in storage.

1 Like

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

1 Like

Hi @Eskild

Correct…but can you post the actual code for the updateBalance function after having modified it for this?

It’s not clear what you mean by this. What would the code for the updateBalance function look like after making this change? Your alternative solution will be clearer if you post the actual code for us to see :slight_smile:

1 Like

Nice solution @glenmiller :ok_hand: … and welcome to the Academy forum!

I’m not sure if you know, but you don’t need the parentheses in this return statement:

return users[inId].balance;

You only need the parentheses when you returning more than one value (separated by commas).

1 Like

Sure. I think I would write it like this :

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

Wouldn’t that work? :slight_smile:

1 Like

The first balance was stored to storage but when updateBalance was run it was telling it only store it to memory. Thus when you ran the getBalance it had already for got updateBalance because it was only stored in memory.

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

It fixes the problem, because the following line of code…

… saves the balance (passed into the updateBalance function) to the user with the id (also passed into the updateBalance function) and which is stored permanently in the mapping users. The new balance will overwrite the user’s existing balance, which is currently stored at users[id].balance . As the balance is modified directly in the mapping (permanent storage), the update isn’t lost once the function has finished executing (like it was when only saved to memory).

As the updateBalance function only needs to update exisiting balances for users that already exist, the following line of code isn’t needed…

…because all this is doing is repeating the functionality of the addUser function. If we are updating the balance for an existing user, the id will already have been assigned to the user (and established as the key to retrieve that particular user’s data from the mapping) in the addUser function. Also, by its very nature, the id doesn’t need to be updated like the balance. We only need to pass id as a parameter into the updateBalance function because, as the key, it is needed to locate the correct user in the mapping, so that its balance can be reassigned and saved.

So, in summary, we only want to update the mapping’s permanent storage with a user’s new balance, which can be achieved with just the second line of code in your updateBalance function body , and also as shown in this post.

I hope that’s clear. Just let us know if you have any questions.

Keep on learning! :muscle:

1 Like

Yep, that’s perfect! :slight_smile:

2 Likes

Yes Jon, started to learn solidity… This is the direction I would like to move in. I will do in JS the assignment that we were discussing couple of weeks ago but currently I am working and don’t have so much free time. So I would like to spend this free time learning Solidity and about blockchain :slight_smile: .
Thank you for the feedback, and I understand. The public visibility does this for us. Hope you are doing well too!

1 Like

Thanks for the clarification.

I am curios, can a smart contract create another smart contract ? For example each contract would be a separate person.
I don’t know how exactly MakerDAO works, but I believe that when you create new Vault, it creates a new smart contract that can interact with that vault. Is that correct?

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

}```
1 Like

Great! :smiley:

Not really… the fact that the updateBalance function is marked public, means that it can be called from an external service, such as Remix (and not just from within smart contracts). I meant that we are using updateBalance as a setter (so we don’t want to return anything). We already have a getter to do that (getBalance), which returns the user’s current balance (so this will be the newBalance if the original balance has been updated using updateBalance). I hope that’s clear :sweat_smile:

Enjoy your Solidity journey! I’m sure you’re finding all your hard work with JavaScript has helped a lot, as there are quite a few similarities.

2 Likes

Possibilities for programming are almost endless, meaning yes, you could program a smart contract that create a child smart contract for specific purposes like your example.

You can read more about how MakerDAO Smart Contracts works here: MCD Docs - Smart Contract Annotations.

Hope you find this useful.
If you have any more questions, please let us know so we can help you! :slight_smile:

Carlos Z.

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

There are multiple ways of solving the issue one being that you could change the memory scope to storage so that the person can be saved.

However I would go for the solution of using the same instance object in the map and simply update its value. Also I would tighten up the security by forcing there to be a person who exists for the given id by using a require

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

The problem with the updateBalance() function is that it stores the new balance in memory, and it keeps it only inside the function, in order to update the balance globally we need to store the new balance globally, that means that we have to initiate the variable “user” in storage instead of memory

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

First I tried to use the same strategy as in the previous video: use an insert function. I didn’t succeed, then I just change the 16th line changing “memory” with “storage”
User storage user = users[id];

1 Like
         User storage user = users[id];
1 Like