Forward all gas on PullPayment withdrawal (#1976)

* Add withdrawWithGas

* Improve docs

* Add changelog entry

* Update contracts/payment/PullPayment.sol

Co-Authored-By: Francisco Giordano <frangio.1@gmail.com>

* Remove repeated comment

* Update changelog entry

* Fix inline docs

* Fix changelog formatting

(cherry picked from commit d6e10ab786)
release-v2.4.0
Nicolás Venturo 5 years ago committed by Francisco Giordano
parent ab4d43ce14
commit 1c220e175d
  1. 8
      CHANGELOG.md
  2. 25
      contracts/payment/PullPayment.sol
  3. 28
      contracts/payment/escrow/Escrow.sol
  4. 12
      test/payment/PullPayment.test.js

@ -6,11 +6,19 @@
* `Address.toPayable`: added a helper to convert between address types without having to resort to low-level casting. ([#1773](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1773))
* Facilities to make metatransaction-enabled contracts through the Gas Station Network. ([#1844](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1844))
* `Address.sendValue`: added a replacement to Solidity's `transfer`, removing the fixed gas stipend. ([#1962](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1962))
* Added replacement for functions that don't forward all gas (which have been deprecated): ([#1976](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1976))
* `PullPayment.withdrawPaymentsWithGas(address payable payee)`
* `Escrow.withdrawWithGas(address payable payee)`
### Improvements:
* `Address.isContract`: switched from `extcodesize` to `extcodehash` for less gas usage. ([#1802](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1802))
* `SafeMath`: added custom error messages support for `sub`, `div` and `mod` functions. `ERC20` and `ERC777` updated to throw custom errors on subtraction overflows. ([#1828](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1828))
### Deprecations:
* Deprecated functions that don't forward all gas: ([#1976](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1976))
* `PullPayment.withdrawPayments(address payable payee)`
* `Escrow.withdraw(address payable payee)`
### Breaking changes in drafts:
* `SignatureBouncer` has been removed from the library, both to avoid confusions with the GSN Bouncers and `GSNBouncerSignature` and because the API was not very clear. ([#1879](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1879))

@ -25,17 +25,34 @@ contract PullPayment {
/**
* @dev Withdraw accumulated payments.
* @param payee Whose payments will be withdrawn.
*
* Note that _any_ account can call this function, not just the `payee`.
* This means that contracts unaware of the `PullPayment` protocol can still
* receive funds this way, by having a separate account call
* {withdrawPayments}.
*
* NOTE: This function has been deprecated, use {withdrawPaymentsWithGas}
* instead. Calling contracts with fixed gas limits is an anti-pattern and
* may break contract interactions in network upgrades (hardforks).
* https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more.]
*
* @param payee Whose payments will be withdrawn.
*/
function withdrawPayments(address payable payee) public {
_escrow.withdraw(payee);
}
/**
* @dev Same as {withdrawPayments}, but forwarding all gas to the recipient.
*
* WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities.
* Make sure you trust the recipient, or are either following the
* checks-effects-interactions pattern or using {ReentrancyGuard}.
*/
function withdrawPaymentsWithGas(address payable payee) public {
_escrow.withdrawWithGas(payee);
}
/**
* @dev Returns the payments owed to an address.
* @param dest The creditor's address.
@ -46,11 +63,11 @@ contract PullPayment {
/**
* @dev Called by the payer to store the sent amount as credit to be pulled.
* @param dest The destination address of the funds.
* @param amount The amount to transfer.
*
* Funds sent in this way are stored in an intermediate {Escrow} contract, so
* there is no danger of them being spent before withdrawal.
*
* @param dest The destination address of the funds.
* @param amount The amount to transfer.
*/
function _asyncTransfer(address dest, uint256 amount) internal {
_escrow.deposit.value(amount)(dest);

@ -2,6 +2,7 @@ pragma solidity ^0.5.0;
import "../../math/SafeMath.sol";
import "../../ownership/Secondary.sol";
import "../../utils/Address.sol";
/**
* @title Escrow
@ -18,6 +19,7 @@ import "../../ownership/Secondary.sol";
*/
contract Escrow is Secondary {
using SafeMath for uint256;
using Address for address payable;
event Deposited(address indexed payee, uint256 weiAmount);
event Withdrawn(address indexed payee, uint256 weiAmount);
@ -40,7 +42,14 @@ contract Escrow is Secondary {
}
/**
* @dev Withdraw accumulated balance for a payee.
* @dev Withdraw accumulated balance for a payee, forwarding 2300 gas (a
* Solidity `transfer`).
*
* NOTE: This function has been deprecated, use {withdrawWithGas} instead.
* Calling contracts with fixed-gas limits is an anti-pattern and may break
* contract interactions in network upgrades (hardforks).
* https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more.]
*
* @param payee The address whose funds will be withdrawn and transferred to.
*/
function withdraw(address payable payee) public onlyPrimary {
@ -52,4 +61,21 @@ contract Escrow is Secondary {
emit Withdrawn(payee, payment);
}
/**
* @dev Same as {withdraw}, but forwarding all gas to the recipient.
*
* WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities.
* Make sure you trust the recipient, or are either following the
* checks-effects-interactions pattern or using {ReentrancyGuard}.
*/
function withdrawWithGas(address payable payee) public onlyPrimary {
uint256 payment = _deposits[payee];
_deposits[payee] = 0;
payee.sendValue(payment);
emit Withdrawn(payee, payment);
}
}

@ -42,4 +42,16 @@ contract('PullPayment', function ([_, payer, payee1, payee2]) {
(await balanceTracker.delta()).should.be.bignumber.equal(amount);
(await this.contract.payments(payee1)).should.be.bignumber.equal('0');
});
it('can withdraw payment forwarding all gas', async function () {
const balanceTracker = await balance.tracker(payee1);
await this.contract.callTransfer(payee1, amount, { from: payer });
(await this.contract.payments(payee1)).should.be.bignumber.equal(amount);
await this.contract.withdrawPaymentsWithGas(payee1);
(await balanceTracker.delta()).should.be.bignumber.equal(amount);
(await this.contract.payments(payee1)).should.be.bignumber.equal('0');
});
});

Loading…
Cancel
Save