8 min read

Address

Introduction

In the world of Ethereum, an address is a primitive data type that is used to represent the intended recipient for a message. This is similar to a postal address, where the label on a letterbox corresponds to the name of the person and their location.

In Ethereum, an address is linked to a public key and is unique due to the use of public key cryptography. The private key is stored in a wallet and is used to control access to any Ether in the account, as well as to sign transactions to spend any funds in the account.

On Ethereum, an address may look like this:
0xd8da6bf26964af9d7eed9e03e53415d37aa96045

There are two types of Ethereum addresses:

  • Externally Owned Accounts (EOAs)
  • Contract Accounts (Smart Contracts).

EOAs are controlled by their private key, while Smart Contracts are controlled by their code. EOA are typically controlled by end users and currently as of March 5th 2023 are the only accounts that can initiate a transaction on Ethereum.

Address Data Type Overview

In Solidity, an address is a 20-byte data type that is specifically designed to hold account addresses in Ethereum. These addresses are 160 bits or 20 bytes in size and can hold both contract addresses and externally owned account addresses. The address is a value type, and it creates a new copy while being assigned to another variable.

There are two variations for the address type introduced after Solidity 0.5.0:

  • address: This is the general address type that can hold Ethereum addresses as part of a contract. It cannot be used to send or receive Ether. They are meant for address management within smart contracts but cannot be used for receiving and sending Ether as part of smart contracts.
  • payable address: These are similar to the address type with the additional capability of receiving as well as sending Ether to other accounts. It has additional methods, send() and transfer(). Since the payable address is a superset of the address type, it can implicitly be converted into a simple address; however, the reverse needs an explicit conversion.

The idea was to make the distinction between addresses that can receive money, and those who can’t and used for other purposes. Simply address payable can receive Ether, while a regular address cannot.

pragma solidity 0.8.16;

contract MyContract {
    address public owner;
    address payable public recipient;
    uint public balance;
    
    constructor() {
        owner = msg.sender;
        recipient = payable(owner);
    }
    
    function sendEther() public payable {
        balance += msg.value;
    }
    
    function transferEther() public {
        recipient.transfer(balance);
        balance = 0;
    }
}

In this smart contract we have:

  • a regular address variable owner and a payable address variable recipient.
  • The sendEther() function allows the contract to receive Ether, which is stored in the balance variable.
  • The transferEther() function transfers the accumulated Ether to the recipient address using the transfer() method, which is only available to payable addresses.

Address Data Type Methods and Functions

Payable address methods

The payable address provides the following two functions to transfer Ether and is often used with call function:

  • transfer: The transfer function is used to transfer Ether from the current contract to another address. It is the preferred method for transferring Ether as it raises an exception and returns the Ether to the caller if the transfer fails.
  • send: The send function is similar to the transfer function, but it returns a Boolean value depending on the successful execution of the Ether transfer.
  • call: The call function in Solidity is a low-level function that allows contracts to invoke external contracts or execute arbitrary code. call takes a bytes parameter and returns a boolean value indicating the success or failure of the operation, as well as any output data. The call method also has a value field that allows for transferring Ether along with the function call. However, care must be taken when using call as it can be vulnerable to re-entrancy attacks if not used properly. It is generally recommended to use higher-level functions like transfer or send for transferring Ether.
pragma solidity 0.8.16;

contract PaymentContract {
    address payable public recipient;
    uint public balance;
    
    constructor(address payable _recipient) {
        recipient = _recipient;
    }
    
    function sendEther() public payable {
        balance += msg.value;
    }
    
    function sendWithSend() public {
        require(balance > 0, "Insufficient balance.");
        recipient.send(balance);
        balance = 0;
    }
    
    function sendWithTransfer() public {
        require(balance > 0, "Insufficient balance.");
        recipient.transfer(balance);
        balance = 0;
    }
    
    function sendWithCall() public {
        require(balance > 0, "Insufficient balance.");
        (bool success, ) = recipient.call{value: balance}("");
        require(success, "Transaction failed.");
        balance = 0;
    }
}

The address type has a balance property that returns the amount of Ether available with the account and has a few functions for invoking functions in other contracts. It also provides the following three functions for invoking the contract function:

  • CALL: The call function is used to call an external function of another contract.
  • DELEGATECALL: The delegate call function is used to call an external function of another contract but executes it in the context of the current contract. Simply it takes some of the calling contract's information, inputs into the called contracts logic and gets a result.
  • CALLCODE: The call code function is used to call an external function of another contract but executes it in the context of the current contract's storage and address space.

We will cover these methods in depth later on.

Notable Global Methods That Uses Address

In Solidity, there are special global variables that are used to often to interact with addresses:

  • msg.sender is a special global variable that represents the address of the contract caller.
  • msg.value is another global variable that represents the amount of Ether sent along with a transaction.

These variables can be used in Solidity functions to perform conditional logic or to modify contract state based on the caller's address or the amount of Ether sent.

Here's an example of how to use msg.sender and msg.value in a Simple Storage Smart Contract:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

contract SimpleStorage {
    address owner;
    uint256 storedData;

    constructor() {
        owner = msg.sender;
    }

    function set(uint256 x) public payable {
        require(msg.sender == owner, "Only the contract owner can set the value.");
        require(msg.value == 1 ether, "Please send 1 Ether along with the transaction to set the value.");
        storedData = x;
    }

    function get() public view returns (uint256) {
        return storedData;
    }
}

In this example:

  • the owner variable is set to msg.sender in the constructor function, ensuring that only the contract owner can call the set function to modify the stored data.
  • The set function also includes a require statement that checks if the caller is the owner of the contract.
  • Another require statement checks if the transaction includes exactly 1 Ether in value. If these conditions are not met, the function will revert and the transaction will fail.
  • The get function simply returns the value stored in the contract, without requiring any special permissions or Ether value.

Using msg.sender and msg.value in Solidity functions allows for conditional logic and access control based on the address of the contract caller and the amount of Ether sent along with a transaction. These global variables are useful tools for building more complex Smart Contracts that can manage access to data and funds on the Ethereum blockchain.

Best Practices for Using Address and Payable Address Data Types

When using address and payable address in Solidity, it is important to follow these best practices:

  1. Use payable address when sending and receiving Ether.
  2. Use transfer() for sending Ether as it is the preferred method due to its safety features.
  3. Always check the return values of send and transfer functions for errors and handle them appropriately.
  4. Be cautious of using CALL or DELEGATECALL to call unknown external contracts as may could be malicious or prone to errors.

Zero Address

When deploying a smart contract on the Ethereum network, a special transaction is used to specify the contract creation. This transaction requires a sender and a recipient address. In the case of contract creation, the recipient address used is known as the zero-address, which is a special address that contains only empty bytes.

The zero-address is like a "fake account" on the Ethereum network. It is still a 20-byte address like any other Ethereum address but has a unique property of containing only 0x0 values.

This makes it easy for:

  • the Ethereum Virtual Machine (EVM) to recognize that a transaction with the zero-address as the recipient is meant for creating a new smart contract.
  • developers to burn tokens by sending them to the zero address no one controls. This can be done with any address that is assured no one controls but usually the zero address suffices.

address()

The address primitive in Solidity 0.8.16 provides a built-in method called address(). This method is used to return the address of the contract currently being executed. It is a read-only property that returns a 20-byte address.

The address() method is often used in Solidity smart contracts for various purposes, such as implementing logic to restrict certain operations to only the contract owner or to check the balance of the contract.

Another interesting use of the address() method is to check if a contract has been deployed at a specific Ethereum address. To do this, you can use the address(0) syntax, which returns the zero-address we mentioned earlier. If the result of address(0) is equal to the address you are checking, it means that there is no contract deployed at that address.

In Solidity 0.8.0 and later versions, the address primitive has a built-in method called address(). This method can be used to retrieve the Ethereum address of a contract instance or an externally owned account (EOA).

For example, in the withdraw function below, the address(this) statement returns the Ethereum address of the current contract instance.

Suppose you have a contract that allows users to withdraw funds from it. You can use the address() method to ensure that only the contract owner can withdraw funds by adding a check in the withdraw() function as follows:

function withdraw(uint amount, address destination) public {
    require(msg.sender == owner, "Only the contract owner can withdraw funds");
    require(address(this).balance >= amount, "Insufficient balance in the contract");
    require(address(destination) != address(0), "Can't send to burn address in this method");
    payable(destination).transfer(amount);
}
  • The withdraw function takes two parameters: amount and destination. It checks if the caller is the owner of the contract, if the contract balance is sufficient to make the withdrawal, and if the destination address is not set to the burn address (0x0000000000000000000000000000000000000000).
  • Then, it uses the payable keyword to make the destination address payable and transfer the specified amount to it using the transfer method.
  • Note that the address(0) value is used to represent the Ethereum burn address, which is a special address that does not have a private key and is used to permanently remove Ether from circulation.

Address Operators

ADD INFORMATION

Conclusion

In conclusion, an Ethereum address is used to represent the intended recipient for a message on the Ethereum network. There are two types of addresses: Externally Owned Accounts (EOAs) and Contract Accounts (Smart Contracts). In Solidity, an address is a 20-byte data type that can hold both contract addresses and externally owned account addresses. There are two variations of the address type introduced after Solidity 0.5.0: address and payable address. Payable address can receive Ether, while address cannot.

Best practices include using payable address when sending and receiving Ether, using transfer() for sending Ether, and checking return values of send and transfer functions for errors. The address() method in Solidity 0.8.16 can be used to retrieve the Ethereum address of a contract instance or an externally owned account, and address(0) is used to represent the Ethereum burn address.

Feedback

Have feedback? Found something that needs to be updated, accurate, or explained?

Join our discord and share what can be improved.

Any and all feedback is welcomed.