Unverified Commit ad18098d by Nicolás Venturo Committed by GitHub

Disallow ERC20._transfer from the zero address. (#1752)

* Add requirement of non-zero from to ERC20 transfer.

* Add test for transferFrom zero address to behavior.

* Create ERC20.transfer behavior.

* Add tests for _transfer.

* Add changelog entry.

* Fix linter error.

* Delete repeated test.

* Fix hardcoded error prefix.

* Update CHANGELOG.md

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

* Address review comments.
parent dd6ec219
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
### Bugfixes: ### 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)) * `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))
* `ERC20._transfer`: the `from` argument was allowed to be the zero address, so it was possible to internally trigger a transfer of 0 tokens from the zero address. This address is not a valid destinatary of transfers, nor can it give or receive allowance, so this behavior was inconsistent. It now reverts. ([#1752](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1752))
## 2.2.0 (2019-03-14) ## 2.2.0 (2019-03-14)
......
...@@ -20,6 +20,10 @@ contract ERC20Mock is ERC20 { ...@@ -20,6 +20,10 @@ contract ERC20Mock is ERC20 {
_burnFrom(account, amount); _burnFrom(account, amount);
} }
function transferInternal(address from, address to, uint256 value) public {
_transfer(from, to, value);
}
function approveInternal(address owner, address spender, uint256 value) public { function approveInternal(address owner, address spender, uint256 value) public {
_approve(owner, spender, value); _approve(owner, spender, value);
} }
......
...@@ -125,6 +125,7 @@ contract ERC20 is IERC20 { ...@@ -125,6 +125,7 @@ contract ERC20 is IERC20 {
* @param value The amount to be transferred. * @param value The amount to be transferred.
*/ */
function _transfer(address from, address to, uint256 value) internal { function _transfer(address from, address to, uint256 value) internal {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address"); require(to != address(0), "ERC20: transfer to the zero address");
_balances[from] = _balances[from].sub(value); _balances[from] = _balances[from].sub(value);
......
...@@ -3,6 +3,7 @@ const { ZERO_ADDRESS } = constants; ...@@ -3,6 +3,7 @@ const { ZERO_ADDRESS } = constants;
const { const {
shouldBehaveLikeERC20, shouldBehaveLikeERC20,
shouldBehaveLikeERC20Transfer,
shouldBehaveLikeERC20Approve, shouldBehaveLikeERC20Approve,
} = require('./ERC20.behavior'); } = require('./ERC20.behavior');
...@@ -330,6 +331,20 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) { ...@@ -330,6 +331,20 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
}); });
}); });
describe('_transfer', function () {
shouldBehaveLikeERC20Transfer('ERC20', initialHolder, recipient, initialSupply, function (from, to, amount) {
return this.token.transferInternal(from, to, amount);
});
describe('when the sender is the zero address', function () {
it('reverts', async function () {
await shouldFail.reverting.withMessage(this.token.transferInternal(ZERO_ADDRESS, recipient, initialSupply),
'ERC20: transfer from the zero address'
);
});
});
});
describe('_approve', function () { describe('_approve', function () {
shouldBehaveLikeERC20Approve('ERC20', initialHolder, recipient, initialSupply, function (owner, spender, amount) { shouldBehaveLikeERC20Approve('ERC20', initialHolder, recipient, initialSupply, function (owner, spender, amount) {
return this.token.approveInternal(owner, spender, amount); return this.token.approveInternal(owner, spender, amount);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment