Design Pattern — Part 2, Pull Payment Pattern

Tushar Bansal
2 min readJul 31, 2023

--

Design patterns have proven to be the go-to solution for many common programming scenarios. They are not only generic, but also easy to extend and tweak to one’s needs. Lets take a look at Pull payment pattern of solidity.

Pull Payment Pattern

A common task when coding smart contracts is to transfer funds. Unfortunately, there are several circumstances under which a transfer can fail. This is due to the fact that the implementation to send funds involves an external call, which basically hands over control to the called contract. Therefore, security considerations regarding external calls and re-entrancy attacks have to be considered. A re-entrancy attack describes
the scenario where the called contract calls back the current contract, before the first invocation of the function containing the call, was finished.

Problem

When a contract sends funds to another party, the send operation
can fail.

contract AuctionProblem {
address payable highestBidder;
uint highestBid;

function bid() public payable {
require(msg.value >= highestBid);
if (highestBidder != address(0)) {
// if call fails causing a rollback,
// no one else can bid
highestBidder.transfer(highestBid);
}

highestBidder = payable(msg.sender);
highestBid = msg.value;
}
}

Solution

Let the receiver of a payment withdraw the funds. Introducing a refunds mapping, which stores the claimable defeated bids, to be withdrawn by participants in a pull payment process.

contract AuctionSolution {
address payable highestBidder;
uint highestBid;
mapping(address => uint) refunds;

function bid() public payable {
require(msg.value >= highestBid);
if (highestBidder != address(0)) {
// record the underlying bid to be refund
refunds[highestBidder] += highestBid;
}
highestBidder = payable(msg.sender);
highestBid = msg.value;
}

function withdrawRefund() public {
uint refund = refunds[msg.sender];
refunds[msg.sender] = 0;
payable(msg.sender).transfer(refund);
}
}

Here, the person who needs to make the withdraw, need to execute the withdrawRefund. Here it first sets the refunds[msg.sender] as zero and then proceeds further. As well know reentracy attach where the attacker took advantage of gas. In AuctionSolution smart contract it wont be possible.

--

--