Hi @skplunkerin,
Your solution works, including your additional functionality to ensure that…
- the addUser function can only add users with IDs that don’t already exist;
- the updateBalance function can only update the balances of existing users; and
- the getBalance function can only be accessed with existing user IDs.
The additional code you’ve added demonstrates an excellent knowledge of the syntax already covered in this course
I now encourage you to work at producing the same high quality functionality with more concise and cleaner code. This is an important consideration with smart contracts, because cleaner, more concise code generally results in a securer smart contract with less potential vulnerabilities or risk of bugs. With Ethereum smart contracts we also need to consider gas consumption, and cleaner, more concise code will usually reduce gas costs.
While it is good practice to place chunks of code which perform distinct operations and computations within their own separate functions, I think there is also a danger of overdoing this. For example, one of the advantages of using a require statement is that it combines the conditional logic of if/else statements with revert
. It is already a concise piece of code, and so placing conditional logic in a separate function, and calling this function from within the require statement, when this condition can be coded more concisely within the require statement itself, is actually defeating the purpose of using a require statement in the first place. Separate helper functions are mostly useful where you have multiple require statements (or in your case assert statements), or more complex conditional logic. Where you have more than one function that needs the same require statement, the best way to avoid code duplication is to place the require statement within a modifier and add the modifier to the function headers.
You also have one situation that should be caught with a require statement, and not an assert statement. If addUser() is called with an ID of zero, the assert statement …
… fails … but for the wrong reason. This assert statement should fail if the user hasn’t been added correctly, but instead it is failing because a user with ID zero has been added. This is easily addressed by adding an additional require statement that prevents an input ID of zero. This is important, because your logic for checking whether a user exists or not is based upon Users[_id].id
evaluating to zero only when the user doesn’t exist.
Have a look at my suggestions of how to address these issues in the following modified version of your solution…
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.7;
contract MemoryAndStorage {
struct User{
uint id;
uint balance;
}
mapping(uint => User) users;
modifier userExists(uint id) {
require(users[id].id != 0, "User doesn't exist. Please, add one first.");
_;
}
function addUser(uint _id, uint _balance) public {
require(_id != 0, "ID cannot be zero");
require(users[_id].id == 0, "User already exists. Please use updateBalance()");
users[_id] = User(_id, _balance);
_assertUserBalance(_id, _balance);
}
function updateBalance(uint _id, uint _balance) public userExists(_id) {
users[_id].balance = _balance;
_assertUserBalance(_id, _balance);
}
function getBalance(uint _id) public view userExists(_id) returns(uint) {
return users[_id].balance;
}
function _assertUserBalance(uint _id, uint _balance) private view {
assert(users[_id].id != 0);
assert(users[_id].balance == _balance);
}
}
Let me know if you have any questions 