• Wed. Aug 17th, 2022

Charlielikes

Computers Tech Games Crypto Music and More

Learn Solidity lesson 12. Addresses and only owner.

ByCharlielikes

Aug 1, 2022 ,

We’ve already seen several types in Solidity, such as uint and strings. Now let’s see a new and very important type: The address. Addresses represent accounts on the blockchain and are therefore widely used.

We have also seen that addresses are derived from the public key. More precisely, the address of an account is the last 20 bytes of the public key hash. Therefore, the address type in Solidity is a 20-byte hexadecimal number.

In the code below, we declare a variable of type address through its literal. The prefix 0x indicates that this is a hexadecimal number.

address myAddress = 0xC66d07097f4823343bf116463070B3be5e941C4E

There is another type of address in Solidity, the address payable. It is possible, in a contract, to send base coin to a certain address, however, for this purpose, that address must be of the payable type.

To declare an address as payable, just use the keyword payable, as follows.

address payable myAddress = 0xC66d07097f4823343bf116463070B3be5e941C4E

Even if we do not declare an address payable, it is possible to send base currency to it. To do this, you need to cast the variable as payable. We will see this in a next lesson.

Let us now understand a little better about Ethereum accounts.

Externally Owned Accounts and contract accounts

Ethereum has two types of accounts: the so-called Externally Owned Accounts (EOA) and contract accounts. Both are designated by an address, but there are major differences between them.

I’ve said several times that accounts on Ethereum are public/private key pairs, but that only goes for EOA. Contract accounts do not have private or public keys.

Why that? Because contracts don’t have to sign anything. Contracts are not capable of initiating transactions, they only react to transactions. This means that nobody owns a contract; once placed on the blockchain, it is public and anyone can interact with it. If the creator wants privileges within the contract, that privilege needs to be explicitly programmed into the contract code. We’ll see how.

Contracts are accounts because, like external accounts, they have a balance in the base coin, that is, they can hold Ether. In fact, contract accounts have 4 pieces of information stored on the blockchain.

  • Its balance.
  • The nonce. A unique number necessary for the proper behaviour of the blockchain (we will explain more later).
  • The bytecode.
  • The value of the state variables defined by the contract.

The bytecode is the contract code, so it must be stored on the blockchain, as well as the contract state variables. The fact that contracts have a balance indicates that we can send Ether to such accounts. But there is a difference: we need to write functions, in the contract, that allow the withdrawal of such Ether. As contracts do not have private keys, they cannot willingly send Ether. Contracts have no will of their own.

It is important to remember that contracts do not initiate transactions.

EOAs only have the first two data fields: the balance and the nonce. They have neither a bytecode nor state variables.

Only Owner

Let’s remember that our last contract was responsible for storing a will on the blockchain. Did you notice that it has a major security flaw? Anyone can invoke the public functions of the contract, and change the amount each heir would receive. A malicious heir, for example, could invoke the function and increase its inheritable amount.

As I said, if the one who wrote the contract wants privileges within the contract, he/she needs to program that into the code. That’s what we’ll do in this section.

To start, let’s declare a state variable of type address, named owner. Add the following line of code to the contract, outside of any function.

address owner;

This creates a new state variable. This variable will store the address of its owner, who will have special privileges. For example, we will require that, in order to change the amount each heir is due, the transaction must be signed by the owner.

Within the contract, it is possible to have access to the address of the account that submitted the transaction. For this, the sender property of the global variable msg is used, as follows.

msg.sender

There is a subtlety in using msg.sender because it is possible for contracts to invoke functions in other contracts, so msg.sender does not always indicate who initiated the transaction. We’ll see that later, we don’t need to worry now.

Now we need to require that the address that sent the transaction that invokes the function addHeir is the same as the owner of the contract. This is done as in the statement below.

require(msg.sender == owner);

The statement is very explanatory and does exactly what we intended. With that, we solved our problem. Let’s see what the new function addHeir looks like.

function addHeir(string memory _name, uint _value) public {
require(msg.sender == owner);
heranca[_name] = _value;
}

When invoked, the first thing the function does is check if the msg.sender is the same address as the owner. If so, the transaction proceeds. Otherwise, the transaction reverts (rolls back).

When a transaction reverts, it does not cause any changes to the state of the blockchain. Basically, the function invoked will have no effect.

This is a “Only Owner” strategy, where only the owner of the contract is able to make certain functions take effect. Anyone can invoke this function, but if they don’t have the owner’s address private key, the invocation will just be a waste of gas fee. It will revert and have no effect.

The constructor

We now need to assign a value to the state variable owner. We want to save the address of the account that deployed the contract. Remember that the deploy of a contract is a transaction, so we can use the msg.sender property to get the address of the account that deployed it.

At deploy time, the contract invokes a special function, which is executed only at that time. It cannot be invoked later. Such a function is called a constructor, and must be declared as follows.

constructor([parameters]) {
// body of the function
}

In our contract, the constructor does not use any parameters, it just stores the address that made the transaction in the variable owner.

constructor() {
owner = msg.sender;
}

The contract is now complete. Below, we can see his full body.

...
mapping(string => uint) inheritance;
address public owner;
constructor() {
owner = msg.sender;
}

function addHeir(string memory _name, uint _value) public {
require(msg.sender == owner);
inheritance[_name] = _value;
}
function recoverInheritance(string memory _name) public view returns (uint) {
return inheritance[_name];
}
...

I changed the visibility of the variable owner to public so that we can verify, in Remix, that it assumes the value of the address that deployed the contract.

In the figure blow, we see that the deploy was done by the address 0x5B3…eddC4.

Deployment is done by a transaction generated by the address 0x5B3…eddC4.

Since the variable owner is public, we can retrieve its value. This is seen in the figure below.

The variable textit{owner} holds the address 0x5B3…eddC4.

As no function was written to change the variable owner, once its value is assigned, it will not be changed.

Also, if we try to invoke the function addHeir using any account other than 0x5B3…eddC4, the transaction will revert. This can be seen in the figure below.

Transactions that invokes the function addHeir, sent by any other address than the owner, will revert.

Another way to assign the address of msg.sender to the variable owner would be to do it at the time of its declaration, as follows.

address owner = msg.sender;

Thus, it would not be necessary to explicitly declare the constructor function. Both methods have the same result.

Constant and immutable variables

We can declare variables as constants and immutable. Both types of variables cannot be changed after their initialization, but constant variables must be initialized at the time of their declaration (at compile time), whereas immutable variables can be initialized in the constructor (at the time of deploy).

An example of a constant variable is as follows.

uint const MY_NUMBER = 42;

Declared this way, the variable MY_NUMBER will hold the value 42 and cannot be changed. In fact, it is not even correct to call MY_NUMBER a variable, as the blockchain does not allocate a dedicated space for it. The value of MY_NUMBER is written directly to the bytecode and is not a state variable.

In the previous example, since the variable owner will not change, can we declare it as constant? Can we make a statement like below?

address public constant owner = msg.sender;

The answer is no. Constants must be initialized at declaration time and defined at compile time. In the above case, the variable owner is only initialized at deploy time. Remember we initialized the variable owner in the constructor? The above statement works as if the variable were initialized in the constructor.

Thus, the variable owner can be declared as immutable, but not as a constant. The sentence below is correct.

address immutable public owner = msg.sender;

Attempting to change the variable owner at any time will generate a compiler error.

Thanks for reading!

Comments and suggestions about this article are welcome.

Any contribution is welcome. www.buymeacoffee.com/jpmorais


Learn Solidity lesson 12. Addresses and only owner. was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.