Solidity Basics

Tjena Filip :slight_smile: Det är lätt att följa dig :slight_smile:

I am wondering if mapping(address => uint) balance; is a contract state as we had before with the state variable?

// Mappings
contract Bank{

// defining the mapping(adress=key balance=uint) named: balance
mapping(address => uint) balance;

// adding more tokens to an adress
// unsigned int named _toAdd (uint cannot be negative) is added to existing balance of address
function addBalance(uint _toAdd) public returns (uint){
    
    // _toAdd uint is then added to the balance of msg.sender(address)
    // same as balance[msg.sender] = balance[msg.sender] + _toAdd
    balance[msg.sender] += _toAdd;
    
    // gives back the updated balance after the transaction within the function scope
    return balance[msg.sender];
    
}

// function to view the new balance (view because we are not changing, just viewing)
function getBalance() public view returns (uint){
    
    // returns updated balance of the adress(msg.sender) 
    return balance[msg.sender];
}

}

1 Like

Hey @Aonia, hope you are ok.

There is a way to return the values from an struct, which can be something like:

struct Person{
  uint id,
  string name,
  uint balance
} 

mapping(address => Person) people;

function getData() public view returns(uint, string memory, uint){
  return(
    people[msg.sender].id,
    people[msg.sender].name,
    people[msg.sender].balance
  );
}

This is an example, you could find a little bit more of info over internet about it.

Carlos Z

Hey @donhuey, hope you are ok.

Please share your code in the following way so I can review it properly and help you solve the issue:

Carlos Z

hii @filip…any error in the code mentioned below?

function withdraw(uint amount,address _address)public {balance[msg.sender]-=amount; address payable tosend=_address;tosend.transfer(amount);}

Some actual tangible, on-hands training. I haven’t seen much yet, but I like what I did. Have to say I like it much better than the ‘topical’ courses such as Blockchain Business Masterclass.

1 Like

Hi @bhallasatyansh13,

function withdraw(uint amount, address _address) public {
    // INCLUDE A SUITABLE REQUIRE STATEMENT HERE
    balance[msg.sender] -= amount;
    address payable tosend = _address;   // ERROR HERE
    tosend.transfer(amount);
}

To be able to assign a non-payable address (such as your parameter _address) to a payable address variable, you also need to explicitly convert the address value you are assigning, as follows…

address payable tosend = payable(_address);

Your function body also needs to start with a require statement to check whether the caller has enough balance to cover the requested withdrawal amount.


Please make sure you format your code before posting it, as described here, so that it’s clearer, easier to read, and easier to debug :slight_smile:

Hi Filip

In your Solidity Programming 101 under - Contract Structure, can you explain the following code

function hello () public pure returns ( string memory ) {

      return " Hello World ";

}

Question-. What’s the difference between returns and return ( without the s)?

Thanks
Eric

1 Like

Hi @ecbwong,

This difference is dictated by the Solidity syntax.

Solidity requires the data types of variables, parameters (function inputs) and returned values (function outputs) to be explicitly defined. This is in contrast to JavaScript, which is a dynamically-typed programming language, where data types do not need to be defined by the programmer in the source code.

These differences are reflected in each programming language’s syntax:

  • In both Solidity and JavaScript the return statement in the function body, which contains, or evaluates to, the actual value(s) returned by the function, is preceded by the keyword return

  • In Solidity, the data type(s) of the value(s) which the function returns (in the return statement) has/have to be defined in the function header using the syntax returns()

In Solidity, in certain circumstances, we also need to declare the data location. That’s why the value returned in your example is explicitly defined as both a string and with the memory data location:  returns(string memory) . The concept of data location, and the circumstances under which it needs to be explicity declared, are introduced later in the course.

Let me know if anything is still unclear, or if you have any further questions.

Hi Jon_m

Thanks for your reply. I get it now. Basically, they are Solidity keywords! I wasn’t sure initially, thinking it might be a typo error but then again ReMix would have highlighted it but didn’t.

As for data storage locations, ‘memory’ storage is temporary- is it not? Once the function is executed, the datas are gone? Well then variables declared globally, will it be perpetual i.e datas will be there whenever we run that particular smart contract anytime in the future?

Appreciate your feedback on that.

Thanks

Eric Wong

1 Like

Hi @ecbwong,

That’s right

Yes

Correct

That’s correct … here you are talking about state variables, which are saved persistently in storage, which is another specific data location. All values stored in state variables are automatically saved in storage, so there is no need to explicity declare their data location with storage. It’s only necessary to declare the data location of local variables, parameters, and return values which are defined as complex data types (strings, arrays and struct instances). This is because, in these specific cases, there is a choice of data location. State variables, local variables, parameters and return values defined with any of the other data types (addresses, Booleans, integers or unsigned integers) have their data location automatically assigned by default, so in these cases there is no need to declare the data location.

There is a specific section of this course which introduces the different types of data location, and the circumstances under which it needs to be explicitly declared. So, you’ll get to see more examples and to practise using it in an assignment.

Thanks very much Jonathan. Very precise answers to each and every question. Appreciate it.

1 Like

I would like a plutus course as well, you have any experience with it @danielmain?

1 Like

Would any of you be so nice as to answer this question for me?:

Why doesn’t the .push function work for me?

 address admin1;
     address admin2;
     address admin3;
     address owner;
    address[] admins;
    function push()private{
        admins[].push([owner,admin1,admin2,admin3]);
    }

Line seven always has a pop up that says "push not found " and “Index expression cannot be ommited”
I haave no idea what those mean :woozy_face: :face_with_raised_eyebrow:

Hi Jonathan

Appreciate very much if you could clarify certain matters in Solidity Basic as regards to the topic on Struct.

I will use Filip video and his codes are as follows

A). Struct Person {
unit age ;
string name;
}

I understand this part being the object blueprint for future object creation.

B). Person [ ] people;

I am ok with this. An array of people variables of Person object.

C). I am lost in his Function addPerson. The body of the function goes like this

Person memory newPerson = Person ( 30, " Bob");
people.push( newPerson);

I am utterly confused with the use of the wordings Person, newPerson and people. In part B, since people variables is the name for Person Class, shouldn’t it be written as

Person memory people = people ( 30, “Bob”);
Person.push ( people);

Could you help me out on Solidity syntax on this because I couldn’t figure out why Filip code in the way he did. I haven’t touched on his getPerson function which is unclear to me too.

Thanks
Eric

1 Like

Hey @jakerichguy,

There are several things wrong with your code …

To push values to an array, you need to call the push method on the array name without the square brackets …

admins.push();

To reference a value within an array we append its index value in square brackets to the array name e.g. admins[0] will reference the first value in the array (at index 0). So, you are getting the error message   Index expression cannot be omitted   because the compiler thinks you want to reference an address within the admins array, and so it’s expecting an index number after the opening square bracket.

Once you remove the square brackets as I’ve shown you above, you’ll now get the error message …

Member "push" not found or not visible after argument-dependent lookup in address[] storage ref.

It’s not clear what is meant by argument-dependent lookup, but essentially, the compiler is informing you that you can’t push an array value …
[owner, admin1, admin2, admin3]
… to the admins array (the address[ ] storage reference). This is because admins is defined as an array containing individual address values e.g.

// address[] admins
[0x74...483, 0x38...819, ...]

… and not as an array containing further arrays of address values e.g.

// address[][] admins
[
   [0x74...483, 0x38...819],
   [0x32...715, 0x44...588],
   ...
] 

Before you remove the square brackets from the array name admins (as above), you also get a slightly different version of this error message. Here, the compiler still thinks you are referencing (looking up) a specific address held in the admins array, and I think it’s basically telling you that you can’t push a value to an address: .push() is an array method that can only be called on an array.

However, even if you also remove the square brackets from the 4 addresses you want to push to the array, you’ll still get the same error message…

admins.push(owner, admin1, admin2, admin3);  // => Error

This is because, as far as I’m aware, you can only use the push method to push a single value at a time to an array e.g.

admins.push(admin1);

The following works, but it is obviously very repetitive and inefficient to code it like this …

admins.push(owner);
admins.push(admin1);
admins.push(admin2);
admins.push(admin3);

As you will already need to have assigned address values to the state variables owner, admin1, admin2 and admin3, my question to you would be: Why do you need to store these addresses in separate state variables AND in an array? Such a duplication of data saved in storage is unnecessarily expensive. I would also think about the following:

  • If you need to store these addresses together in an array, then why not just assign them directly to admins, and remove the need for the separate address state variables altogether?

  • If there is a fixed number of admin addresses that you need to store together in a data structure, then you should define the array as a fixed-sized array instead of dynamically-sized (e.g. address[4] admins;) and assign the admin addresses using index numbers instead of the push method. Or you could think about using a struct instead of an array.

Let us know if anything is unclear, or if you have any further questions. And if your code relates to the Multi-Sig project, then please post your questions in the Project - Multisig Wallet discussion topic instead of here. :slight_smile:

1 Like

Hey Eric,

Exactly right … your “blueprint” analogy is a good one and helpful. I often use the word “template”, which is essentially the same. This “blueprint” that is defined in the struct creates what we can consider to be a customised data-type. So, just as we can declare a variable with a uint or string data-type, for example, we can also declare a variable with a customised data-type using the struct name — in our case, with Person. It’s like a data-type, because it is defining the type of value that the variable can hold: in our case a struct instance (like an object) containing 2 properties (age holding a uint value, and name holding a string value).

This helps us to understand …

… which is a array defined as containing instances (values) created from the Person “blueprint”.

This isn’t correct …
people is the name of the array, which contains instances based on our Person “blueprint” (or Person class, as you have called it here).

To create an instance based on a struct “blueprint”, we need to use the name of the struct. So, this is why we use Person and not the name of the array people in the following line …

We can create a struct instance in a single expression using parentheses. The values assigned to the properties must be placed between the parentheses in the same order as the properties are defined within the struct. This is why we don’t need to declare the property names. As age is the first property within the Person struct, 30 will be automatically assigned to the new instance’s age property etc.

Within the the body of the addPerson function, in the first line (quoted above) a new Person instance is created and assigned to a local variable called newPerson. We could call this local variable anything, apart from the array name people — we are not referencing the array in the first line of code. As you know, all variables in Solidity need to be declared with a specific data-type. Because this local variable is temporarily storing our new Person instance, we need to declare it with our customised data-type, Person . As a struct instance is a complex data-type, we also need to declare the data location of any local variable holding a struct instance.

In this second line we are pushing the newly created Person instance (stored temporarily in the newPerson variable) to the people array. As in JavaScript, the push method is appended to the array it is called on using dot notation. The value to be pushed to (added to the end of) the array is the push method’s argument, and so is placed within the parentheses.

This is wrong, because…

  1. .push() is an array method, and so it can’t be called on a struct. The properties of a struct are fixed according to its definition (a fixed “blueprint”). We can’t add, remove or modify a struct’s properties. We can only modify the property values of individual instances created from the struct “blueprint”.
  2. It’s trying to do the opposite of what we want to do.

Hopefully, this explanation makes things clearer and will also help you to understand the syntax in the getPerson function. But, just let me know if anything is still unclear, or if you have any more questions.

Hi Jonathan

Thanks for the very concise answers. I think I am getting it. Basically my confusion over when to use Person and people is answered in your statement - " To create an instance based on a struct “blueprint”, we need to use the name of the struct. So, this is why we use Person and not the name of the array people"

You also mentioned that ‘newPerson’, an instance of the Person class is a local variable inside the body of addPerson function. Am I right by saying that code

people.push( newPerson)

add the newPerson instance to the people array? Thus allowing getPerson function to retrieve the instances back when required? The newPerson being pushed to the array would be in ‘state’ variable storage?

Thanks
Eric

1 Like

Thank you so much @jon_m ! I think I’ll take your advice and use a struct instead. :+1:

1 Like

Hi Eric,

Glad the answers were helpful :slightly_smiling_face:

Yes … push() works in same way as it does in JavaScript: it adds the value between the parentheses to the end of the array. So, the first newPerson pushed to people will be at index 0 (people[0]), the second newPerson pushed will be at index 1 (people[1]) etc.

Yes … and the getPerson function works by retrieving specific instances according to their index position within the array e.g.

Person memory person = people[_index];
return(person.age, person.name);

or you could code it without using a local variable, as follows …

return(people[_index].age, people[_index].name);

The user calls getPerson() with the index position of the instance they want to retrieve.

Correct … the people array is a state variable and is saved in persistent storage.

Instead of pushing and storing the struct instances (created in addPerson) to an array, we can also assign them to a mapping. In a mapping, each instance would be mapped to a unique key (an address, for example), and so in the getPerson function a specific person would be retrieved based on its key (e.g. msg.sender) instead of its index number.

I’m not sure if you’re covered the section of the course on mappings yet, but you need to watch the videos on mappings to be able to understand my following example:

// Contract State (storage)
Person[] people;  // using an array
mapping(address => Person) people;  // or using a mapping

// addPerson function
Person memory newPerson = Person(30, "Bob");
people.push(newPerson);  // push to an array
people[msg.sender] = newPerson;  // or assign to a mapping

//getPerson function
Person memory person = people[_index]; // retrieve from an array
Person memory person = people[msg.sender]; // retrieve from a mapping
return(person.age, person.name);

As always, just let me know if you have any further questions.

Hi Jonathan,

I am really grateful for your help. I think I can follow much better the course now armed with the new informations provided by you. Yes, just started the video on " Introduction to Mapping". I will definitely seek your help if I do encounter any difficulty further down the road. Thanks a lot.

Eric Wong

1 Like