Solidità - Modello di ritiro

Il modello di prelievo garantisce che non venga effettuata una chiamata di trasferimento diretto, il che rappresenta una minaccia per la sicurezza. Il seguente contratto mostra l'uso insicuro della chiamata di trasferimento per inviare ether.

pragma solidity ^0.5.0;

contract Test {
   address payable public richest;
   uint public mostSent;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         // Insecure practice
         richest.transfer(msg.value);
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
}

Il contratto sopra può essere reso in uno stato inutilizzabile facendo sì che il più ricco sia un contratto con funzione di fallback in errore. Quando la funzione fallback fallisce, fallisce anche la funzione diventaRichest () e il contratto rimarrà bloccato per sempre. Per mitigare questo problema, possiamo utilizzare Withdrawal Pattern.

Nel modello di prelievo, reimposteremo l'importo in sospeso prima di ogni trasferimento. Garantirà che solo il contratto del chiamante fallisca.

pragma solidity ^0.5.0;

contract Test {
   address public richest;
   uint public mostSent;

   mapping (address => uint) pendingWithdrawals;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         pendingWithdrawals[richest] += msg.value;
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
   function withdraw() public {
      uint amount = pendingWithdrawals[msg.sender];
      pendingWithdrawals[msg.sender] = 0;
      msg.sender.transfer(amount);
   }
}