Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mint ERC777 without reception ack #2552

Merged
merged 7 commits into from
Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -21,6 +21,7 @@
* `AccessControl`: Added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562))
* `AccessControlEnumerable`: Fixed `renounceRole` not updated underlying set. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572))
* `ERC1155`: Make `uri` public so overloading function can call it using super. ([#2576](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2576))
* `ERC777`: Make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552))

### How to upgrade from 3.x

Expand Down
10 changes: 10 additions & 0 deletions contracts/mocks/ERC777Mock.sol
Expand Up @@ -27,6 +27,16 @@ contract ERC777Mock is Context, ERC777 {
_mint(to, amount, userData, operatorData);
}

function mintInternalExtended (
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
) public {
_mint(to, amount, userData, operatorData, requireReceptionAck);
}

function approveInternal(address holder, address spender, uint256 value) public {
_approve(holder, spender, value);
}
Expand Down
33 changes: 32 additions & 1 deletion contracts/token/ERC777/ERC777.sol
Expand Up @@ -312,6 +312,37 @@ contract ERC777 is Context, IERC777, IERC20 {
)
internal
virtual
{
_mint(account, amount, userData, operatorData, true);
}

/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If `requireReceptionAck` is set to true, and if a send hook is
* registered for `account`, the corresponding function will be called with
* `operator`, `data` and `operatorData`.
*
* See {IERC777Sender} and {IERC777Recipient}.
*
* Emits {Minted} and {IERC20-Transfer} events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the {IERC777Recipient}
* interface.
*/
function _mint(
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
internal
virtual
{
require(account != address(0), "ERC777: mint to the zero address");

Expand All @@ -323,7 +354,7 @@ contract ERC777 is Context, IERC777, IERC20 {
_totalSupply += amount;
_balances[account] += amount;

_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);

emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
Expand Down
127 changes: 127 additions & 0 deletions test/token/ERC777/ERC777.test.js
Expand Up @@ -171,6 +171,133 @@ contract('ERC777', function (accounts) {
shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData);
});
});

describe('mint (internal extended)', function () {
const amount = new BN('5');

context('to anyone', function () {
beforeEach(async function () {
this.recipient = anyone;
});

context('with default operator', function () {
const operator = defaultOperatorA;

it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});

it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});

context('with non operator', function () {
const operator = newOperator;

it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});

it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});
});

context('to non ERC777TokensRecipient implementer', function () {
beforeEach(async function () {
this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new();
this.recipient = this.tokensRecipientImplementer.address;
});

context('with default operator', function () {
const operator = defaultOperatorA;

it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});

it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});

context('with non operator', function () {
const operator = newOperator;

it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});

it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});
});
});
});

describe('operator management', function () {
Expand Down