From 97a9ca56813d5baafb94c5f6b4b2483cc5d6f72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 23 Apr 2019 19:06:09 -0300 Subject: [PATCH] Add a vault to PostDeliveryCrowdsale. (#1721) * Add a vault to PostDeliveryCrowdsale. * Add changelog entry. * Apply suggestions from code review Co-Authored-By: nventuro * Rename TokenVault. * add solhint ignore directive --- CHANGELOG.md | 3 ++ .../distribution/PostDeliveryCrowdsale.sol | 28 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 408f0e13f..36cbf5433 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ ### Improvements: * Upgraded the minimum compiler version to v0.5.7: this prevents users from encountering compiler bugs that were fixed in this version. ([#1724](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1724)) +### Bugfixes: + * `PostDeliveryCrowdsale`: some validations where skipped when paired with other crowdsale flavors, such as `AllowanceCrowdsale`, or `MintableCrowdsale` and `ERC20Capped`, which could cause buyers to not be able to claim their purchased tokens. ([#1721](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1721)) + ## 2.2.0 (2019-03-14) ### New features: diff --git a/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol b/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol index 6abbffd46..c074ba421 100644 --- a/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol +++ b/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol @@ -2,6 +2,8 @@ pragma solidity ^0.5.7; import "../validation/TimedCrowdsale.sol"; import "../../math/SafeMath.sol"; +import "../../ownership/Secondary.sol"; +import "../../token/ERC20/IERC20.sol"; /** * @title PostDeliveryCrowdsale @@ -11,6 +13,11 @@ contract PostDeliveryCrowdsale is TimedCrowdsale { using SafeMath for uint256; mapping(address => uint256) private _balances; + __unstable__TokenVault private _vault; + + constructor() public { + _vault = new __unstable__TokenVault(); + } /** * @dev Withdraw tokens only after crowdsale ends. @@ -20,8 +27,9 @@ contract PostDeliveryCrowdsale is TimedCrowdsale { require(hasClosed()); uint256 amount = _balances[beneficiary]; require(amount > 0); + _balances[beneficiary] = 0; - _deliverTokens(beneficiary, amount); + _vault.transfer(token(), beneficiary, amount); } /** @@ -32,12 +40,26 @@ contract PostDeliveryCrowdsale is TimedCrowdsale { } /** - * @dev Overrides parent by storing balances instead of issuing tokens right away. + * @dev Overrides parent by storing due balances, and delivering tokens to the vault instead of the end user. This + * ensures that the tokens will be available by the time they are withdrawn (which may not be the case if + * `_deliverTokens` was called later). * @param beneficiary Token purchaser * @param tokenAmount Amount of tokens purchased */ function _processPurchase(address beneficiary, uint256 tokenAmount) internal { _balances[beneficiary] = _balances[beneficiary].add(tokenAmount); + _deliverTokens(address(_vault), tokenAmount); + } +} + +/** + * @title __unstable__TokenVault + * @dev Similar to an Escrow for tokens, this contract allows its primary account to spend its tokens as it sees fit. + * This contract is an internal helper for PostDeliveryCrowdsale, and should not be used outside of this context. + */ +// solhint-disable-next-line contract-name-camelcase +contract __unstable__TokenVault is Secondary { + function transfer(IERC20 token, address to, uint256 amount) public onlyPrimary { + token.transfer(to, amount); } - }