3

Solidity 0.5.0 has separated the "address" type into two types: address payable and address. Only address payable have the address.transfer method. I don't want to use the withdrawal mechanism.

When I store the address in an array, and try to access it later I get an error from solc:

Member "transfer" not found or not visible after argument-dependent lookup in address.

I guess it's because the address that I stored is not of type "address payable" (it wouldn't hurt to make this error more clear btw). But it is said in the documentation that the address returned by msg.sender is of type "address payable". So I don't quite understand. It is also said that a conversion using the type uint160 is possible, but I don't know how. Any help would be greatly appreciated

Here is a useless sample code which reproduce the problem:

pragma solidity >0.4.99 <0.6.0;
contract simple{

  address payable[] private addressIndex;
  uint price = 100;

  /* Distribute all the fund to the clients */
  function distribute() private returns (uint) {

    uint amount = (address(this).balance-1) / addressIndex.length;
    for (uint i = 0; i < addressIndex.length; i++) {
      addressIndex[i].transfer(amount); //error
    }
    return amount;
  }

  function () external payable {

    if ( msg.value != price ) { revert(); }
    addressIndex.push(msg.sender);
    distribute();
  }

}

EDIT 1: With the Solidity compiler solc v0.5.0, I had to declare all "address" variables which will receive fund in the future as "address payable". For security reasons no implicit conversion of the type "address" will be made to "address payable". Only "address payable" have the ".transfer" method. Therefore my addressIndex array of address should be declared as:

address payable[] addressIndex;

2 Answers 2

7

UPDATE:

Solidity 0.6.x now have explicit conversions to payable:

payable(userAddress)

And for smart contracts:

payable(address(smartContract))

Old answer for Solidity 0.5.x:

Here is how to cast address to address payable:

address(uint160(userAddress))

And contract cast to address payable (thx to @Cyril):

address(uint160(address(smartContract)));
1
  • 1
    The very funny thing is when you have to do it with a contract: address payable contractAddress = address(uint160(address(contract)));
    – Cyril
    Commented Jan 22, 2019 at 8:42
0

I was unable to replicate the reported issue.

You need ; here

uint price = 1

and there is no initial_price, so I changed it price which is initialized to 1. I changed that to 100 so it's divisible. Also rewrote the fallback function so it's easier on the eyes.

  function () external payable {

    require( msg.value == price );
    addressIndex.push(msg.sender);
    distribute();
  }

It actually worked when I sent it the required 100 Wei.

Here it is in Remix so you can see it working.

enter image description here

I think possibly you want to set the price much higher because it's expressed in Wei and a single Wei cannot be further divided. It's possible you have an issue with rounding (truncation, actually) but it might be acceptable to carry over the leftovers for the next round.

I'm more concerned about the iterative process that does the distribution. There are some non-obvious issues here.

One issue is the possibility of trapping funds in the contract when it becomes too expensive to iterate over the list.

Another issue is the likely accidental DoS if/when one of the receivers refuses the transfer. Refusal by one denies the transfer to all, and refusal is the default for a contract. So, basically, if a contract sends funds and that contract doesn't have a payable fallback function, it's game over for all funds sent to the contract from that point forward. This is another way that all funds (including future funds) would be trapped in the contract.

Since it can happen quite innocently, it's possible to do it intentionally (attack). The withdrawal pattern solves for most of that. A transfer() inside of a loop should make your spidey-sense tingle. While it is seldom good to carry on after a failed transfer, you could consider using .send instead because return false instead of reverting. It's hard to say what is best without more context.

Hope it helps.

1
  • Sorry about the code error, It's a simplification of my real code, where there is no problem about the price variable. I found the solution to my problem (see publication edit). But thank you for the very useful insight on how to distribute funds and the possible issues in my code, I am definitely going to change the distribution process
    – user18359
    Commented Nov 13, 2018 at 1:49

Your Answer

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