Solidity Basics

Hey @NoosphereLabs,

When I type  "   or   (   my Remix text editor automatically adds the closing quotation marks or closing parenthesis after the cursor. Also, whenever I hit RETURN , my cursor automatically jumps to the same indentation on the next line. I imagine these are the default key bindings that Remix comes with. Does yours not come with these? Personally, I’ve never felt the need to add any additional ones, but from the link I sent you, it seems like you can if you already have some knowledge about how to configure key bindings, and it sounds like you’ve already made some progress with your research :+1:

My Remix default settings don’t automatically add a semi colon to the end of each line of code I start typing. To me this makes sense, because not every new line will be a statement that requires one (e.g. function headers).

I guess, if you have your own particular key binding preferences, then you could code your contracts in your text editor of choice, and configure your key bindings there. You can then copy and paste your contracts into Remix to compile and deploy them. However, I can think of a couple of disadvantages with this method, which, while you’re still learning Solidity, will probably end up wasting more time than you save with your key bindings…

  • Writing your contracts directly within Remix has the advantage of being able to compile and check your code for errors as you code (set your Compiler Configuration to Auto compile in the Solidity Compiler panel). If you copy and paste your contracts from a different text editor into Remix, then, initially, you will probably have several compiler errors that you still need to correct before deployment.
  • Copying and pasting often fails to reproduce the same indentation, and so you would probably still need to make some further adjustments to your code in Remix anyway.
1 Like

Hi @michael356,

The contract you have posted needs to be deployed with a string (entered into the input box next to the orange Deploy button). From what you have explained, I assume you are already doing this with the string "Hello World!"

The constructor is only called on deployment, and so if you don’t have another function which you can call to change the string stored in the message state variable, whenever you call the hello() function, it will always output "Hello World!"

For example, if we add the following function to your contract …

function updateMessage(string memory _message) public {
    message = _message;
}

… we can then do the following …

  1. Deploy the contract with "Hello World!" — this assigns "Hello World!" to the message state variable.
  2. Call hello() — this outputs the message "Hello World!"
  3. Call updateMessage() with a different string — this assigns the new string to the message state variable, replacing "Hello World!"
  4. Call hello() — this now outputs the new message.

Let me know if this answers your question and resolves your issue, or whether you still have questions :slight_smile:

You are also missing the view keyword in the hello() function header. You should get an orange/yellow compiler warning highlighting this.

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)

Let me know if you have any questions.

Hi @B_S,

Leaving aside the similarities and differences of how mappings and arrays are compiled into bytecode and stored by the EVM — which, due to the peculiarities of the EVM, isn’t the same as how arrays and maps in other programming languages are saved in computer memory — let’s consider the similarities and differences that determine how, on a more practical level, arrays and mappings are used by Solidity developers to write their smart contract code…

The values stored in an array are ordered according to a fixed sequence of indices (0,1, 2, 3 etc.). Each index number and its associated value is a key/value pair (each unique key being an index number). This means that, as well as being able to reference or retrieve values by their specific index number (if we know it), arrays can also be iterated over. The ability to iterate over an array, means we can search for specifc data stored within it, without needing to know specific index numbers (keys). In Solidity, only fixed-size arrays can have values added to specifc index numbers. To add values to a dynamic-sized array, they have to be pushed using the .push() array method.

Mappings are also data structures which store key/value pairs. However, unlike arrays, values can be mapped to unique keys of data-types other than uint e.g. addresses or strings. There is, therefore, no need for values to be added and stored in a mapping according to a fixed, sequential order of integers, which must begin with 0.

Because a mapping can be defined with uint keys, values could potentially be added and mapped according to a fixed sequence of uint keys which mirror an array’s index values. However, this would most probably be an inefficient use of a mapping, and it is generally accepted that mappings cannot be iterated over (although, strictly speaking, this doesn’t seem to me to be completely true). In Solidity, mappings are commonly used to map values to Ethereum addresses, and in this case, they cannot be iterated over. In order to add, modify, reference or retrieve a value mapped to an address, this unique address needs to be known. This gives mappings the ability to provide more in-built data privacy than arrays.

Generally speaking, if you need to store and look up large amounts of data, using mappings will save you a noticeable amount of gas (as opposed to using arrays); and, as you say, usually …

With large amounts of data, using arrays, where mappings could provide the same results, runs the risk of transactions running out of gas and failing, because the gas limit has been reached.

I hope this gives you a clearer picture of the differences between mappings and arrays in Solidity. Let me know if you have any further questions.

1 Like

@jon_m thanks for such a thorough response!

arrays, unlike mappings, can be iterated over their VALUES…:exploding_head:

are there type rules for mappings? e.g. do the keys (and/or values) in each mapping have to be of the same type as each other? or is it possible to have strings in some and uint in some and int in some, etc, all within the same mapping structure?

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: