From e7b22483afd1ab0a75271bf768185e261c524eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Mon, 16 Mar 2020 19:49:17 -0300 Subject: [PATCH] Make ERC777 operator the caller (#2134) * Make the sender the operator * Make hook methods private * Add changelog entry --- CHANGELOG.md | 2 ++ contracts/mocks/ERC777Mock.sol | 5 ++--- contracts/token/ERC777/ERC777.sol | 23 ++++++++++++----------- test/token/ERC777/ERC777.behavior.js | 10 ++++++---- test/token/ERC777/ERC777.test.js | 2 +- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe7e2a2edb..4d94c23c552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * `Escrow`, `ConditionalEscrow`, `RefundEscrow`: these now use `Ownable` instead of `Secondary`, their external API changed accordingly. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) * `ERC20`: removed `_burnFrom`. ([#2119](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2119)) * `Address`: removed `toPayable`, use `payable(address)` instead. ([#2133](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2133)) + * `ERC777`: `_send`, `_mint` and `_burn` now use the caller as the operator. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134)) + * `ERC777`: removed `_callsTokensToSend` and `_callTokensReceived`. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134)) ## 2.5.0 (2020-02-04) diff --git a/contracts/mocks/ERC777Mock.sol b/contracts/mocks/ERC777Mock.sol index da89a15a048..40602b09ff3 100644 --- a/contracts/mocks/ERC777Mock.sol +++ b/contracts/mocks/ERC777Mock.sol @@ -11,16 +11,15 @@ contract ERC777Mock is Context, ERC777 { string memory symbol, address[] memory defaultOperators ) public ERC777(name, symbol, defaultOperators) { - _mint(_msgSender(), initialHolder, initialBalance, "", ""); + _mint(initialHolder, initialBalance, "", ""); } function mintInternal ( - address operator, address to, uint256 amount, bytes memory userData, bytes memory operatorData ) public { - _mint(operator, to, amount, userData, operatorData); + _mint(to, amount, userData, operatorData); } } diff --git a/contracts/token/ERC777/ERC777.sol b/contracts/token/ERC777/ERC777.sol index 26e03efb987..587651d2542 100644 --- a/contracts/token/ERC777/ERC777.sol +++ b/contracts/token/ERC777/ERC777.sol @@ -135,7 +135,7 @@ contract ERC777 is Context, IERC777, IERC20 { * Also emits a {IERC20-Transfer} event for ERC20 compatibility. */ function send(address recipient, uint256 amount, bytes memory data) public override { - _send(_msgSender(), _msgSender(), recipient, amount, data, "", true); + _send(_msgSender(), recipient, amount, data, "", true); } /** @@ -166,7 +166,7 @@ contract ERC777 is Context, IERC777, IERC20 { * Also emits a {IERC20-Transfer} event for ERC20 compatibility. */ function burn(uint256 amount, bytes memory data) public override { - _burn(_msgSender(), _msgSender(), amount, data, ""); + _burn(_msgSender(), amount, data, ""); } /** @@ -233,7 +233,7 @@ contract ERC777 is Context, IERC777, IERC20 { public override { require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); - _send(_msgSender(), sender, recipient, amount, data, operatorData, true); + _send(sender, recipient, amount, data, operatorData, true); } /** @@ -243,7 +243,7 @@ contract ERC777 is Context, IERC777, IERC20 { */ function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public override { require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); - _burn(_msgSender(), account, amount, data, operatorData); + _burn(account, amount, data, operatorData); } /** @@ -311,7 +311,6 @@ contract ERC777 is Context, IERC777, IERC20 { * interface. */ function _mint( - address operator, address account, uint256 amount, bytes memory userData, @@ -321,6 +320,8 @@ contract ERC777 is Context, IERC777, IERC20 { { require(account != address(0), "ERC777: mint to the zero address"); + address operator = _msgSender(); + // Update state variables _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); @@ -333,7 +334,6 @@ contract ERC777 is Context, IERC777, IERC20 { /** * @dev Send tokens - * @param operator address operator requesting the transfer * @param from address token holder address * @param to address recipient address * @param amount uint256 amount of tokens to transfer @@ -342,7 +342,6 @@ contract ERC777 is Context, IERC777, IERC20 { * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient */ function _send( - address operator, address from, address to, uint256 amount, @@ -355,6 +354,8 @@ contract ERC777 is Context, IERC777, IERC20 { require(from != address(0), "ERC777: send from the zero address"); require(to != address(0), "ERC777: send to the zero address"); + address operator = _msgSender(); + _callTokensToSend(operator, from, to, amount, userData, operatorData); _move(operator, from, to, amount, userData, operatorData); @@ -364,14 +365,12 @@ contract ERC777 is Context, IERC777, IERC20 { /** * @dev Burn tokens - * @param operator address operator requesting the operation * @param from address token holder address * @param amount uint256 amount of tokens to burn * @param data bytes extra information provided by the token holder * @param operatorData bytes extra information provided by the operator (if any) */ function _burn( - address operator, address from, uint256 amount, bytes memory data, @@ -381,6 +380,8 @@ contract ERC777 is Context, IERC777, IERC20 { { require(from != address(0), "ERC777: burn from the zero address"); + address operator = _msgSender(); + _callTokensToSend(operator, from, address(0), amount, data, operatorData); // Update state variables @@ -437,7 +438,7 @@ contract ERC777 is Context, IERC777, IERC20 { bytes memory userData, bytes memory operatorData ) - internal + private { address implementer = ERC1820_REGISTRY.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH); if (implementer != address(0)) { @@ -465,7 +466,7 @@ contract ERC777 is Context, IERC777, IERC20 { bytes memory operatorData, bool requireReceptionAck ) - internal + private { address implementer = ERC1820_REGISTRY.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH); if (implementer != address(0)) { diff --git a/test/token/ERC777/ERC777.behavior.js b/test/token/ERC777/ERC777.behavior.js index ffddbe96f3d..3ba80c0a79f 100644 --- a/test/token/ERC777/ERC777.behavior.js +++ b/test/token/ERC777/ERC777.behavior.js @@ -281,7 +281,9 @@ function shouldBehaveLikeERC777InternalMint (recipient, operator, amount, data, shouldInternalMintTokens(operator, recipient, amount, data, operatorData); it('reverts when minting tokens for the zero address', async function () { - await expectRevert.unspecified(this.token.mintInternal(operator, ZERO_ADDRESS, amount, data, operatorData)); + await expectRevert.unspecified( + this.token.mintInternal(ZERO_ADDRESS, amount, data, operatorData, { from: operator }) + ); }); } @@ -290,7 +292,7 @@ function shouldInternalMintTokens (operator, to, amount, data, operatorData) { const initialTotalSupply = await this.token.totalSupply(); const initialToBalance = await this.token.balanceOf(to); - const { logs } = await this.token.mintInternal(operator, to, amount, data, operatorData); + const { logs } = await this.token.mintInternal(to, amount, data, operatorData, { from: operator }); expectEvent.inLogs(logs, 'Minted', { operator, @@ -332,7 +334,7 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am it('mint (internal) reverts', async function () { await expectRevert.unspecified( - this.token.mintInternal(operator, this.recipient, amount, data, operatorData) + this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator }) ); }); }); @@ -387,7 +389,7 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () { const { tx } = await this.token.mintInternal( - operator, this.recipient, amount, data, operatorData, + this.recipient, amount, data, operatorData, { from: operator } ); const postRecipientBalance = await this.token.balanceOf(this.recipient); diff --git a/test/token/ERC777/ERC777.test.js b/test/token/ERC777/ERC777.test.js index 830e406ac92..a4a61c27b52 100644 --- a/test/token/ERC777/ERC777.test.js +++ b/test/token/ERC777/ERC777.test.js @@ -300,7 +300,7 @@ describe('ERC777', function () { it('mint (internal) reverts', async function () { await expectRevert( - this.token.mintInternal(operator, this.recipient, amount, data, operatorData), + this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator }), 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', ); });