Solidity Basics

You can use a mapping that forward to a struct.

Something like

struct Person{
uint id;
string name;
int age;
}

mapping (address => Person) Clients;

Carlos Z

1 Like

Hi Jon:

Thanks for your reply and the help.

I spend almost 2 hours to find out the problem is the Remix complier.

Once you deploy a contract and call a function in Remix,

The first contract will always on the top in the Remix window unless you closed it.

I didn’t notice that, so the first contract I wrote 's output is “hello World!” ,so no matter what I changed the contract and depolyed later , it was underneath the first contract.
The first contract 's window won’t fold itself,so i can’t see it,

Once i closed the contract before, only the latest, it works.

Even it’s such a weird reason still useful cause it won’t occur to me if I don’t practice.

Even I think the course is really great ,I think it will be better if there is more practice in the course .

Thanks !

1 Like

Hi @B_S,

Yes … Solidity requires the data types of variables (including arrays and mappings), 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.

Although uncommon, it is possible in JavaScript to have arrays containing values of different data types. In Solidity, however, we can only define arrays to hold values of the same data-type. The keys (array indices) will always be uint values, and so these don’t need to be defined separately e.g.

string[] myDynamicArray;   // Dynamically-sized array containing string values
uint[3] myFixedArray;      // Fixed-size array containing 3 unsigned integers

We can also define arrays to hold struct instances. In this case, the data type is a struct definition (like a customised data-type which we can define with properties of our choosing) e.g.

struct Employee {
    string name;
    uint age;
    bool IsPartTime;
}

Employee[] employees;

Defining an array of struct instances enables us to store values of different types within the same array. However, the first level of values within the array still contains values of the same data-type: a pre-defined “template” of properties (a struct).

We can even define arrays to hold other arrays, although each “sub-array” has to contain the same data-type as all the other “sub-arrays” e.g.

bool[2][] arrayA;   Dynamically-sized array containing fixed-size arrays of 2 Booleans

uint[][8] arrayB;   Fixed-size array containing 8 dynamically-sized arrays of unsigned integers

Moving on to mappings …

In a Solidity mapping, all of the keys have to be of the same data-type, as do the values mapped to those keys. However, the keys can be defined as a different data-type to the values, so they both have to be defined separately e.g.

mapping(uint => uint) points;
mapping(address => uint) balances;

As with arrays, we can also define mappings…

(1) to hold struct instances e.g.

/* Employee struct instances (based on the Employee struct from above) 
   mapped to addresses */
mapping(address => Employee) workforce;  

Also see @thecil’s example.

(2) or to hold arrays (or even arrays of arrays) e.g.

mapping(address => bool[2]) workType;
e.g. true/false (part-time worker)  true/false (works from home)

mapping(uint => Employee[]) teams;
e.g. a dynamically-sized array of employees (a team) mapped to a manager’s ID

So, no … but you can include struct instances all created from the same struct “template”, meaning that you can store property values of different data types, but each struct instance stored in the mapping has to contain the same properties, in the same order, as all of the others.

Let me know if you have any further questions :slight_smile:

2 Likes

Hi @michael356,

I’m glad you’ve finally got things working! :sweat_smile:

I don’t think the problem you’ve been having is the compiler. It sounds more like it’s a problem on deployment — having more than one contract deployed at once, or deploying the wrong contract.

It’s easiest, and better practice, if you only include one contract in each .sol file.
In Remix, you can have several files open (tabs) but the code of only one file can be displayed in the Editor at a time (the highlighted tab when you click on it). If you only have one contract in each file, then it’s the contract displayed in the Editor which will be the only one you can deploy when you click the orange Deploy button in the Deploy and Run Transactions panel on the left: this will be the only contract displayed in the Contract field with the dropdown above the button. (When you come on to inheritance later in the course, you will have more than one contract in this dropdown, and you’ll need to make sure you select the one you want to deploy — but don’t worry about that yet.)

After you’ve deployed a contract, displayed it’s function calls (with the buttons) in the panel and finished interacting with it, I think it’s best to click the delete (bin) icon next to “Deployed Contracts” to reset everything. This will prevent you from having more than one contract deployed at a time, which can get confusing, especially when the contracts have the same, or similar, function names! Towards the end of the course you will want to have more than one contract deployed at a time, but I would keep things simple to start with. But if you do want to have more than one contract open at the same time, you can hide the contract(s) you aren’t interacting with in the panel by clicking on the arrow next to the contract name and address. You can also just remove individual contracts (instead of resetting everything) by clicking on the cross to the right of the contract name and address, instead of the bin icon.

Yes … practice, practice, practice. Experiment with the code from the lectures (and later from the assignments) yourself. Try to re-write the contracts from memory, and add your own variations and modifications. That way, you can get more out of the course material :slight_smile:

you are a Beast when it comes to thorough explanations; i hope you don’t get burned out because all i can offer in return is my engagement and this little heart vote!

1 Like

haha … your engagement and good questions deserve some engaging and thorough explanations :muscle:

1 Like

Hi Jon:

Very useful ideas.

I think hide the contracts will be useful and I will give each contract a more obvious different names.

Thanks again !

1 Like

for some reason, i didn’t see this message in the forum notifications! i don’t know what happened, but i saw it here; i wanted to say thanks for your effort, and i’m glad we reconnected in the TA forum

1 Like

Nice explanation on structs, I have not worked with structs before or the equivalent you mentioned I JavaScript but after further studying I am starting to get it

1 Like

Good to hear @cosimokryptos :muscle:

… and welcome to the forum!

If you have any questions during the course, post them in the discussion topic for the section or assignment you are on, and we’ll do our best to help you out :slight_smile:

Im getting a stop sign at the first hurdle :rofl:
Remix doesnt let me make any new files. Maybe the introduction video is a bit old compared to the latest remix? It says workspace…so Im trying to make a new file like you did “hello world”, but Im unable to name it or do anything. I also made the compiler 0.7.5, should I use the latest instead?

1 Like

Hi @decadent,

In the File Explorers panel, below the Workspaces field where it says “default_workspace”, click on the first little icon in the row of icons, which should say “Create New File” when you hover over it.

A new file should appear at the bottom of the list with an empty field. Click on the empty field and type the filename with the file extension .sol

Click enter, and the new file should automatically open in the text editor as a new tab. Then just start coding :slightly_smiling_face:

Yes, that’s what is probably confusing you. The latest version will have a different user interface to the one used when the video was recorded.

You can also create new folders (the little folder icon, 2nd in the row), and additional Workspaces by clicking on the  +  icon (first in the row of little Workspace icons). This will open a Create Workspace pop-up box, where you can click OK to use the default workspace name, or change it to one of your choosing first.

The compiler version should set itself automatically to whatever version is in the pragma statement in the file which is currently displayed. If for some reason it doesn’t, then you can manually select the one you want from the dropdown in the Solidity Compiler panel.

It is fine to use v0.7.5, the same one used in the videos. But as you’ve seen, v0.8 has been released since then, and you can use the latest if you prefer. There are changes to the syntax in v0.8, but the only one that affects what is taught in this introductory 101 course, is the following:
(This probabaly won’t make much sense to you at the moment, but it will later in the course. And if you have Auto compile turned on, which I recommend because it highlights any errors while you code, then the compiler should prompt you with an error message detailing this change whenever you need to use it. To turn on Auto compile, go to the Solidity Compiler panel, and check the appropriate box under Compiler Configuration)

*****   v0.8 Syntax Change  *****
Prior to Solidity v0.8 msg.sender is already a payable address by default. This means that with v0.7, whenever you want to use msg.sender as a payable address, you don’t have to explicitly convert it.
However, from v0.8 msg.sender is non-payable by default, and so with v0.8, whenever you need to use msg.sender as a payable address, this does require an explicit conversion using the syntax payable(msg.sender)

Just let me know if you still experience problems with this, if anything is unclear, or if you have any other questions :slight_smile:

1 Like

Thank you Jon for taking the time to help out, greatly appriciated. I had problems renaming the file, when I typed there wouldn’t be any letters. All sorted out now and the show goes on :smile:

1 Like

What is the spreadsheet in the first Gas module for?

1 Like

Hey @cosimokryptos,

This spreadheet is just to give you an idea of how a transaction’s gas consumption is calculated. Each low level operation (these are the Op Codes in column A) consumes a predetermined number of gas units (column I).

Don’t worry too much about it. This spreadsheet is quite old now and I don’t know how accurate it still is. And where it says gas cost, this is actually gas consumption in units of gas. This is then multiplied by the gas price in gwei, which fluctuates according to the market, to give you the actual gas cost. But the calculation of gas costs is changing with Ethereum 2.0.

All you need to understand from this spreadsheet is that, historically, gas costs have been based on which individual low level operations are performed during a transaction. Each type of operation consumes a predetermined number of units of gas.

Rather than the spreadsheet, I think articles such as the following one, are more helpful when it comes to learning about how gas costs are calculated…

https://medium.com/coinmonks/understanding-gas-in-ethereum-53ad816f79ae

Let me know if you have any more questions :slight_smile:

When I have “PURE” in this example, I get the following

But when I dont have PURE, it only shows the result here.

Since the varible is in the function, should I not get the same result, even if PURE is envoked or not? Is it only a difference here in REMIX, or would it be a difference in how the contract interacted on the blockchain?

1 Like

Hi @Tomaage,

This is a good question, and shows you’re really thinking about what’s actually happening.

Notice that the compiler is generating a warning (orange indicator) when you leave the function type as undefined (i.e. without the pure, view, or payable keyword). The contract still compiles (there isn’t a red error) and, as you have found out, you can deploy it and call the function without any problem. But if you leave the function type as undefined (or mark it as payable) when you call it from a Web3 client in the front-end (which is what you’re doing, here, from Remix) you are signing and broadcasting a transaction to the network. This costs gas, and on the mainnet it takes time for the transaction to be included in a block, for the block to be mined or validated, and for the block to be added to the blockchain. So, only the transaction hash will be returned to the Web3 client more or less immediately. You can see the transaction hash and the gas cost in the same transaction receipt that you’ve opened in the Remix terminal.

In the front-end we can use asynchronous web3.js code to return the transaction receipt, but this isn’t generated until the block containing the transaction has been added to the blockchain. And even then, we should wait for a certain number of block confirmations before any data in the transaction receipt regarding state changes can be relied upon with any confidence. It’s possible to use asynchronous web3.js code to listen out for these confirmation events as well, but I actually don’t think it’s possible for return values from a function which has changed the contract state to be returned to the Web3 client anyway. This may have changed, and I do need to check the finer details and perform some tests, but the usual way for a Web3 client to retrieve updates regarding changes to the contract state, as soon as they are confirmed on the blockchain, is by including events in our smart contract, which will emit and log the updates we want to track, and which we can then capture in the front-end using web3.js event listeners.

If the Web3 client only needs to retrieve data, and doesn’t need to modify the state of the smart contract, then we don’t need to spend gas on sending a transaction. We normally do this by marking the function with the view keyword, because the data we are retrieving normally involves reading the contract state (i.e. the data stored in the contract’s state variables or mappings). In your particular example we are demonstrating the pure function type. A function should be marked pure when it doesn’t need to change or read the contract state. But you can probably see why we wouldn’t normally want to retrieve data in our front-end which hasn’t been derived in some way from the data stored in the contract state. However, even though it doesn’t make much sense from a practical point of view, the hello() function serves as a useful, and easy to understand, theoretical example of a pure function.

If a Web3 client calls a function marked view or pure, the return value(s) will be sent to it immediately. This is what is reflected in Remix, and why a view (or pure) function will output its return value(s) below the blue function-call button, but an undefined function (orange button) or payable function (red button) will only show any return values as decoded output in the transaction receipt in the terminal.

As well as the issues of (i) gas cost, and (ii) the ease of receiving return data in the front-end, choosing the right function type is also important to (iii) reduce security risk by only permitting each function to access and change what it needs to.

Just to be clear, there are 4 function types (3 of which we define with specific keywords in the function header) as follows:

  1. pure (most restrictive)
    Cannot change or read the contract state.
  2. view
    Can read the contract state, but cannot change it.
  3. undefined (no type declared in the function header)
    Can read and change the contract state (but cannot receive ether)
  4. payable (least restrictive)
    Can read and change the contract state, and can also receive ether.
    (We will look at payable later in the course)

I hope this clarifies things. Just let me know if you have any further questions :slight_smile:

1 Like

Hi Jon,

Thank you for such a detailed and well written answer to my question. I think I understand it now. Absolutly amazing to have such a response in a forum and it only makes me more determined to study on .

Have a merry christmas/holiday

1 Like

Hey @Tomaage,

That’s great that you found my explanation helpful. I often try to provide some background information, and tackle any wider implications, in my answers, because I think that helps students understand and process the new information more easily, especially during this introductory course where it can be harder to make those all important connections between different concepts. Also, by asking one specific question, you often find you need others answereing as well, even though you didn’t know it :smile:

Thanks for your appreciation, and please do use the forum to ask any other questions you may have with the course material. You can also learn a lot by looking at some of the other students’ posts, and the answers and feedback they’ve received, in these forum discussion topics. You may find answers to your own specific questions, but if not, you’re sure to still learn something new.

Merry Christmas to you too :slight_smile:

Hey Academy! I have a quick question…so I’m at the Loops section of “Smart Contract Programming 101”, and see the use case of using something like the while loop when iterating on something. But if you need a counter function, is it more efficient to do the loop example @filip walks us through or is it better to just add number + 10 as the output? Is this because JavaScript is not a strongly typed language, so you have to do the loop to ensure it’s an integer being worked on?

pragma solidity 0.7.5;

contract HelloWorld {
    
    function count(int number) public pure returns(int){
        int i = 0;
        // only do the while loop 10 times
        while(i < 10) {
            number++;
            i++;
        }
        return number;
    }
}

Cool to learn about loops in Solidity, just wondering about the difference between a counter loop like this and an addition operator to an integer variable input

1 Like