63

I saw a Solidity smart contract where some variables were declared with the type address and some were declared with the type address payable. What are the differences between the two?

  • Do they store the same data?
  • How do you convert a value from one address type to the other type?

3 Answers 3

74

Data storage and representation in the EVM

The address and address payable types both store a 160-bit Ethereum address. The concept of payable and non-payable addresses only exists in the Solidity type system at compile-time. The difference between payable and non-payable addresses is gone in the compiled contract code.


Built-in methods

You can use .transfer(..) and .send(..) on address payable, but not on address.

You can use a low-level .call{value: ..}(..) on both address and address payable, even if you attach value.


Casting from address payable to address

address payable can be implicitly or explicitly cast to address:

address payable addr1 = payable(msg.sender);
address addr2 = addr1; // This is correct
address addr3 = address(addr1); // This is correct

Casting from address to address payable

address can only be explicitly cast to address payable:

address addr1 = msg.sender;
address payable addr2 = addr1; // Incorrect
address payable addr3 = payable(addr1); // Correct since Solidity >= 0.6.0
address payable addr4 = address(uint160(addr1)); // Incorrect since Solidity >= 0.8.0

Casting address[] or address payable[]

Although a single address payable can be cast to address, arrays of one address type cannot be cast to arrays of another address type:

function testCast(address payable[] memory _addresses) returns (address[] memory)
{
    return _addresses; // Type error!
}

Rationale

Types in programming languages exist for two reasons:

  • To provide special type-specific semantics (e.g. + on numbers is addition, but + on strings might be concatenation)

  • To limit the amount of incorrect programs that are accepted by the compiler

The splitting of addresses into address and address payable serves the latter purpose. It forces the smart contract programmer to think about whether an address should ever receive ether from the smart contract. If it should never receive Ether, the address type can be used. Compilation will fail with a type error if the programmer makes a mistake and tries to transfer Ether to that address.


Built-in address constants

msg.sender is an address

tx.origin is an address

block.coinbase is an address payable

6
  • 6
    I would ad that type address payable was added in Solidity 0.5.0, and prior to this release, address type worked as address payable. Commented Apr 27, 2019 at 17:38
  • 3
    Note: Since Solidity 0.6.0, you can cast to address payable like this: payable(...)
    – Jesbus
    Commented Dec 27, 2019 at 20:21
  • 4
    Note: Since Solidity 0.8.0, address literals such as address(0x8aB27....) have the type address instead of address payable
    – Jesbus
    Commented Dec 22, 2020 at 13:55
  • 1
    Also, since Solidity 0.8.0, change msg.sender.transfer(x) to payable(msg.sender).transfer(x). Source: docs.soliditylang.org/en/v0.8.2/…
    – peizhao
    Commented Oct 2, 2021 at 23:43
  • i don't get it address payable addr4 = payable(addr1);. why should i state payable on the variable and also cast the address itself to a payable address. what is the purpose of setting the variable as payble if the second one is what adds the functionality of transfer and send. thanks
    – fedesc
    Commented Feb 2, 2022 at 15:26
14

Yes, they store the same data, that is a valid ethereum address.

The difference is that the compiler (at compile time) when encounter an “address payable” is ready to allow (if required in the following code) that address to access primitives useful to manage ethers (namely call, transfer, send).

In that sense, after a declaration like:

address payable spender;

the compiler shall accept any

spender.send();
spender.transfer();               (1)
spender.call();

and generates the bytecode required to implement them.

On the other hand if the declaration is:

address spender;

the compiler shall generate error if any of the (1) is encountered.

After the compile time there is not difference between payable or not address. In particular they have the same size in memory and are not distinguishable.

In short it is a congruency tag, nothing more.

Understanding this, it is very simple to convert any address payable to address and viceversa using cast:

address payable spender = msg.sender;
address owner;
address payable newspender;

owner = address(spender);   // this is no more payable
newspender = address( uint160(owner) );    // this is again payable

As you can see to do the opposite (not payable to payable) it is required explicitly to use uint160 as intermediate casting

Added after 0.6.0 solidity update:

now you have the possibility to directly cast any address to a payable address using the “payable” keyword:

address payable spender = msg.sender;
address owner;
address payable newspender;

owner = address(spender);   // this is no more payable
newspender = payable(owner);    // this is again payable
2
  • Hello, checking if (payable address == address) is always giving me false, even if the addresses are equals... Are they supposed to be checked differently?
    – camillo777
    Commented Oct 17, 2021 at 8:41
  • Cast the payable address to a non-payable address format before to check it.If (address (<payableAddressHere>) = <non-payable-address-here>) then…
    – Rick Park
    Commented Oct 18, 2021 at 12:04
2

I was confused about the payable address but after a lot of searching, I find the best use case of a payable address. when you are working with a solidity smart contract then you use the payable function which receives ethers and stores it in your contract, not your personal address. so if you want to directly send ethers from the contract to your address then you must need to make your address payable.

Example:

address payable contractDeployer;

function recieveEth()public payable{

require(msg.value>0, "amount should be greater then Zero");

// now you directly transfer msg.value to "contractDeployer" account by using builtin function "transfer"

payable(contractDeployer).transfer(msg.value);

}

I hope this answer will be useful for you guys.

Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.