Withdrawal pattern

The withdrawal pattern is also known as a pull-over-push pattern. In this pattern, ether or token transfer (push) from the contract is avoided; rather, the user is allowed to pull ether or token from the contract.

There can be many contracts in which you want to send ether or token to multiple addresses or to a group of addresses. Sending ether from a contract via iterative or non-iterative methods is always going to cause problems, so this should be avoided. Consider the following  DividendContract sample code of the contract. Using this contract, you can distribute the dividend to your investors:

contract DividendContract is Ownable {

address[] public investors;

function registerInvestor(address _investor) public onlyOwner {
require(_investor != address(0));
investors.push(_investor);
}

//Bad Practice
function distributeDividend() public onlyOwner {
for(uint i = 0; i < investors.length; i++) {
uint amount = calculateDividend(investors[i]);
investors[i].transfer(amount); //Push ether to user
}
}

function calculateDividend(address _investor) internal returns(uint) {
//Dividend calculation here
}
}

The contract maintains the list of investors in the investors array. Only the owner of the contract can add investors who are eligible to receive the dividend. The ether present in the contract will be distributed as dividends. As you can see in the distributeDividend() function, there is an unbounded loop and it will iterate the number of times equals the investors array length. There could be multiple issues in the preceding code, such as the following:

  • In the future, there could be an out-of-gas exception due to this loop as the amount of gas consumption would increase linearly according to the increase in the investor list, hence it's dangerous to do this iteratively. There is no way to remove an investor from an array; hence, once the loop starts consuming more gas units than a block gas limit, the transaction to this function call will always fail. It is dangerous to write loops like this way. This will lead to the locking of ether in the contract, assuming that there is no other way to take funds out of the contract.
  • It is possible that the address of an investor is a contract address, which has a continually failing fallback function. This leads to whole transaction failure for the distributeDividend() function call each time. This also leads to the locking of ether in the contract.

To avoid the issues described in the preceding text, the contract should be designed in a way that a user should be able to claim their dividend from the contract. This way, it's the onus of the user to pull the funds. As you can see in the following code, we have the claimDividend() function, that can be called by anyone. However, only the user who has valid balances present in the contract can claim dividends, as shown in the following:

//Good Practice - Pull ether
function
claimDividend() public {
uint amount = balances[msg.sender];
require(amount > 0);
//Ensure to update balance before transfer
//to avoid reentrancy attack
balances[msg.sender] = 0;
msg.sender.transfer(amount)
;
}

The contract should maintain or update the balances of each user or investor accordingly, so that they can withdraw the balance amount from the contract, as, in the preceding code, the balances mapping must be updated via other functions.

Let's take a look at when the withdrawal pattern should be applied.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.45.194