正确地实现以太币转账

原文地址:https://medium.com/@kidinamoto/how-to-send-ether-correctly-a60208ad76d9

Compare send, call & transfer

Let’s see the following code

pragma solidity ^0.4.13; 
contract someContract { 
      mapping(address => uint) balances; 
     
      function deposit() payable {
            balances[msg.sender] += msg.value; 
      } 
 
      //VERY very bad below  
      function withdrawVeryBad1(uint amount) {
        if(balances[msg.sender] >= amount){
          msg.sender.send(amount); balances[msg.sender] -= amount; 
       } 
      } 
 function withdrawVeryVeryBad2(uint amount){
       if(balances[msg.sender] >= amount) { 
       msg.sender.call.gas(2500000).value(amount)(); 
       balances[msg.sender] -= amount; 
  } 
}  
function withdrawVeryVeryBad3(uint amount) { 
  if(balances[msg.sender] >= amount) {        if(msg.sender.call.gas(2500000).value(amount)()) {                balances[msg.sender] -= amount; 
   } 
  } 
} 
function withdrawBad1(uint amount) { 
  if(balances[msg.sender] >= amount) { 
if(msg.sender.send(amount)) { 
balances[msg.sender] -= amount; 
     } 
   } 
} 
function withdrawOkayish(uint amount) { 
     if(balances[msg.sender] >= amount) { 
          balances[msg.sender] -= amount;    if(!msg.sender.send(amount)) { throw; } 
   } 
 } 
function withdrawBad2(uint amount) { 
    if(balances[msg.sender] >= amount) { 
        balances[msg.sender] -= amount;   if(!msg.sender.call.gas(2500000).value(amount)()) { throw; } 
 } 
} 
//OKAY FUNCTIONS 
function withdrawOK(uint amount) { 
    if(balances[msg.sender] >= amount) { 
     balances[msg.sender] -= amount; 
msg.sender.transfer(amount); 
  } 
} 
//New Exception handling 
function withdrawGood(uint amount) { 
    require(balances[msg.sender] >= amount); 
    balances[msg.sender] -= amount;
    msg.sender.transfer(amount); 
  }
}

In the first

function withdrawVeryBad1(uint amount) {
        if(balances[msg.sender] >= amount){
          msg.sender.send(amount); 
          balances[msg.sender] -= amount; 
       } 
 }

The problem is that we do not check if the return value of send is false or not. If the msg.sender is a contract, it will runs out of gas(> 2300), then it will return False. But we will still reduce the balance because there is no exception.

function withdrawVeryVeryBad2(uint amount){
       if(balances[msg.sender] >= amount) { 
       msg.sender.call.gas(2500000).value(amount)(); 
       balances[msg.sender] -= amount; 
  } 
}

In this example, we can run call again and again until the contract has no ether left. the stack size is 1024 so we can do it 1024 times. it’s very bad to reduce the balance after send .

function withdrawVeryVeryBad3(uint amount) { 
  if(balances[msg.sender] >= amount) {        if(msg.sender.call.gas(2500000).value(amount)()) {                    balances[msg.sender] -= amount; 
   } 
  } 
}

veryverybad3 is also not good because it only use if statement.

function withdrawBad1(uint amount) { 
  if(balances[msg.sender] >= amount) { 
    if(msg.sender.send(amount)) { 
       balances[msg.sender] -= amount; 
     } 
   } 
}

if msg.sender is a contract, send will run out of gas. So the function can only work at some rare cases.again, we shouldn’t reduce the balance after.

function withdrawOkayish(uint amount) { 
     if(balances[msg.sender] >= amount) { 
          balances[msg.sender] -= amount;    if(!msg.sender.send(amount)) { throw; } 
   } 
 }

withdrawOkayish is much better, but still not working with re-entrence. the problem is still we are sending 2300 gas.

msg.sender.call.gas(2500000).value(amount)()

does the same as

msg.sender.transfer(amount);

but transfer propagate exceptions.

we can have a require statement instead of if.

require(balances[msg.sender] >= amount);
  • Ethereum

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.

你可能感兴趣的:(智能合约)