From 376820d55cbf193e470186e8be733b3e211a3737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 7 May 2019 17:05:12 -0300 Subject: [PATCH] Hardcode ERC777 granularity to 1, remove tests. (#1739) * Hardcode ERC777 granularity to 1, remove tests. * Add clarifying title comment. --- contracts/drafts/ERC777/ERC777.sol | 13 +- contracts/mocks/ERC777Mock.sol | 3 +- test/drafts/ERC777/ERC777.behavior.js | 3 - test/drafts/ERC777/ERC777.test.js | 639 ++++++++++++-------------- 4 files changed, 295 insertions(+), 363 deletions(-) diff --git a/contracts/drafts/ERC777/ERC777.sol b/contracts/drafts/ERC777/ERC777.sol index 80609666b..ab2e53dae 100644 --- a/contracts/drafts/ERC777/ERC777.sol +++ b/contracts/drafts/ERC777/ERC777.sol @@ -8,7 +8,7 @@ import "../../utils/Address.sol"; import "../IERC1820Registry.sol"; /** - * @title ERC777 token implementation + * @title ERC777 token implementation, with granularity harcoded to 1. * @author etsvigun , Bertrand Masius */ contract ERC777 is IERC777 { @@ -25,8 +25,6 @@ contract ERC777 is IERC777 { uint256 private _totalSupply; - uint256 private _granularity; - bytes32 constant private TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); @@ -43,14 +41,10 @@ contract ERC777 is IERC777 { constructor( string memory name, string memory symbol, - uint256 granularity, address[] memory defaultOperators ) public { - require(granularity > 0); - _name = name; _symbol = symbol; - _granularity = granularity; _defaultOperatorsArray = defaultOperators; for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) { @@ -182,7 +176,7 @@ contract ERC777 is IERC777 { * @return uint256 granularity */ function granularity() public view returns (uint256) { - return _granularity; + return 1; } /** @@ -228,7 +222,6 @@ contract ERC777 is IERC777 { internal { require(to != address(0)); - require((amount % _granularity) == 0); // Update state variables _totalSupply = _totalSupply.add(amount); @@ -260,7 +253,6 @@ contract ERC777 is IERC777 { { require(from != address(0)); require(to != address(0)); - require((amount % _granularity) == 0); _callTokensToSend(operator, from, to, amount, userData, operatorData); @@ -291,7 +283,6 @@ contract ERC777 is IERC777 { private { require(from != address(0)); - require((amount % _granularity) == 0); _callTokensToSend(operator, from, address(0), amount, data, operatorData); diff --git a/contracts/mocks/ERC777Mock.sol b/contracts/mocks/ERC777Mock.sol index 30657ca2e..bb4c4ead2 100644 --- a/contracts/mocks/ERC777Mock.sol +++ b/contracts/mocks/ERC777Mock.sol @@ -8,9 +8,8 @@ contract ERC777Mock is ERC777 { uint256 initialBalance, string memory name, string memory symbol, - uint256 granularity, address[] memory defaultOperators - ) public ERC777(name, symbol, granularity, defaultOperators) { + ) public ERC777(name, symbol, defaultOperators) { _mint(msg.sender, initialHolder, initialBalance, "", ""); } diff --git a/test/drafts/ERC777/ERC777.behavior.js b/test/drafts/ERC777/ERC777.behavior.js index 771f4a1f2..a2b16073e 100644 --- a/test/drafts/ERC777/ERC777.behavior.js +++ b/test/drafts/ERC777/ERC777.behavior.js @@ -524,10 +524,7 @@ module.exports = { shouldBehaveLikeERC777DirectSendBurn, shouldBehaveLikeERC777OperatorSendBurn, shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, - shouldDirectSendTokens, - shouldDirectBurnTokens, shouldBehaveLikeERC777InternalMint, - shouldInternalMintTokens, shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, shouldBehaveLikeERC777SendBurnWithSendHook, }; diff --git a/test/drafts/ERC777/ERC777.test.js b/test/drafts/ERC777/ERC777.test.js index 7cc609917..8a12539b8 100644 --- a/test/drafts/ERC777/ERC777.test.js +++ b/test/drafts/ERC777/ERC777.test.js @@ -4,10 +4,7 @@ const { shouldBehaveLikeERC777DirectSendBurn, shouldBehaveLikeERC777OperatorSendBurn, shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, - shouldDirectSendTokens, - shouldDirectBurnTokens, shouldBehaveLikeERC777InternalMint, - shouldInternalMintTokens, shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, shouldBehaveLikeERC777SendBurnWithSendHook, } = require('./ERC777.behavior'); @@ -30,419 +27,367 @@ contract('ERC777', function ([ this.erc1820 = await singletons.ERC1820Registry(registryFunder); }); - it('reverts with a granularity of zero', async function () { - await shouldFail.reverting(ERC777.new(holder, initialSupply, name, symbol, 0, [])); - }); + context('with default operators', function () { + beforeEach(async function () { + this.token = await ERC777.new(holder, initialSupply, name, symbol, defaultOperators); + }); - context('with a granularity of one', function () { - const granularity = new BN('1'); + it.skip('does not emit AuthorizedOperator events for default operators', async function () { + expectEvent.not.inConstructor(this.token, 'AuthorizedOperator'); // This helper needs to be implemented + }); - context('with default operators', function () { - beforeEach(async function () { - this.token = await ERC777.new(holder, initialSupply, name, symbol, granularity, defaultOperators); + describe('basic information', function () { + it('returns the name', async function () { + (await this.token.name()).should.equal(name); }); - it.skip('does not emit AuthorizedOperator events for default operators', async function () { - expectEvent.not.inConstructor(this.token, 'AuthorizedOperator'); // This helper needs to be implemented + it('returns the symbol', async function () { + (await this.token.symbol()).should.equal(symbol); }); - describe('basic information', function () { - it('returns the name', async function () { - (await this.token.name()).should.equal(name); + it('has a granularity of 1', async function () { + (await this.token.granularity()).should.be.bignumber.equal('1'); + }); + + it('returns the default operators', async function () { + (await this.token.defaultOperators()).should.deep.equal(defaultOperators); + }); + + it('default operators are operators for all accounts', async function () { + for (const operator of defaultOperators) { + (await this.token.isOperatorFor(operator, anyone)).should.equal(true); + } + }); + + it('returns thte total supply', async function () { + (await this.token.totalSupply()).should.be.bignumber.equal(initialSupply); + }); + + it('is registered in the registry', async function () { + (await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC777Token'))) + .should.equal(this.token.address); + }); + }); + + describe('balanceOf', function () { + context('for an account with no tokens', function () { + it('returns zero', async function () { + (await this.token.balanceOf(anyone)).should.be.bignumber.equal('0'); + }); + }); + + context('for an account with tokens', function () { + it('returns their balance', async function () { + (await this.token.balanceOf(holder)).should.be.bignumber.equal(initialSupply); + }); + }); + }); + + context('with no ERC777TokensSender and no ERC777TokensRecipient implementers', function () { + describe('send/burn', function () { + shouldBehaveLikeERC777DirectSendBurn(holder, anyone, data); + + context('with self operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, holder, data, operatorData); }); - it('returns the symbol', async function () { - (await this.token.symbol()).should.equal(symbol); + context('with first default operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorA, data, operatorData); }); - it('returns the granularity', async function () { - (await this.token.granularity()).should.be.bignumber.equal(granularity); + context('with second default operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorB, data, operatorData); }); - it('returns the default operators', async function () { + context('before authorizing a new operator', function () { + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); + }); + + context('with new authorized operator', function () { + beforeEach(async function () { + await this.token.authorizeOperator(newOperator, { from: holder }); + }); + + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, newOperator, data, operatorData); + + context('with revoked operator', function () { + beforeEach(async function () { + await this.token.revokeOperator(newOperator, { from: holder }); + }); + + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); + }); + }); + }); + + describe('mint (internal)', function () { + const to = anyone; + const amount = new BN('5'); + + context('with default operator', function () { + const operator = defaultOperatorA; + + shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); + }); + + context('with non operator', function () { + const operator = newOperator; + + shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); + }); + }); + }); + + describe('operator management', function () { + it('accounts are their own operator', async function () { + (await this.token.isOperatorFor(holder, holder)).should.equal(true); + }); + + it('reverts when self-authorizing', async function () { + await shouldFail.reverting(this.token.authorizeOperator(holder, { from: holder })); + }); + + it('reverts when self-revoking', async function () { + await shouldFail.reverting(this.token.revokeOperator(holder, { from: holder })); + }); + + it('non-operators can be revoked', async function () { + (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); + + const { logs } = await this.token.revokeOperator(newOperator, { from: holder }); + expectEvent.inLogs(logs, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); + + (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); + }); + + it('non-operators can be authorized', async function () { + (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); + + const { logs } = await this.token.authorizeOperator(newOperator, { from: holder }); + expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); + + (await this.token.isOperatorFor(newOperator, holder)).should.equal(true); + }); + + describe('new operators', function () { + beforeEach(async function () { + await this.token.authorizeOperator(newOperator, { from: holder }); + }); + + it('are not added to the default operators list', async function () { (await this.token.defaultOperators()).should.deep.equal(defaultOperators); }); - it('default operators are operators for all accounts', async function () { - for (const operator of defaultOperators) { - (await this.token.isOperatorFor(operator, anyone)).should.equal(true); - } - }); - - it('returns thte total supply', async function () { - (await this.token.totalSupply()).should.be.bignumber.equal(initialSupply); - }); - - it('is registered in the registry', async function () { - (await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC777Token'))) - .should.equal(this.token.address); - }); - }); - - describe('balanceOf', function () { - context('for an account with no tokens', function () { - it('returns zero', async function () { - (await this.token.balanceOf(anyone)).should.be.bignumber.equal('0'); - }); - }); - - context('for an account with tokens', function () { - it('returns their balance', async function () { - (await this.token.balanceOf(holder)).should.be.bignumber.equal(initialSupply); - }); - }); - }); - - context('with no ERC777TokensSender and no ERC777TokensRecipient implementers', function () { - describe('send/burn', function () { - shouldBehaveLikeERC777DirectSendBurn(holder, anyone, data); - - context('with self operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, holder, data, operatorData); - }); - - context('with first default operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorA, data, operatorData); - }); - - context('with second default operator', function () { - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorB, data, operatorData); - }); - - context('before authorizing a new operator', function () { - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); - }); - - context('with new authorized operator', function () { - beforeEach(async function () { - await this.token.authorizeOperator(newOperator, { from: holder }); - }); - - shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, newOperator, data, operatorData); - - context('with revoked operator', function () { - beforeEach(async function () { - await this.token.revokeOperator(newOperator, { from: holder }); - }); - - shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); - }); - }); - }); - - describe('mint (internal)', function () { - const to = anyone; - const amount = new BN('5'); - - context('with default operator', function () { - const operator = defaultOperatorA; - - shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); - }); - - context('with non operator', function () { - const operator = newOperator; - - shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); - }); - }); - }); - - describe('operator management', function () { - it('accounts are their own operator', async function () { - (await this.token.isOperatorFor(holder, holder)).should.equal(true); - }); - - it('reverts when self-authorizing', async function () { - await shouldFail.reverting(this.token.authorizeOperator(holder, { from: holder })); - }); - - it('reverts when self-revoking', async function () { - await shouldFail.reverting(this.token.revokeOperator(holder, { from: holder })); - }); - - it('non-operators can be revoked', async function () { - (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); - - const { logs } = await this.token.revokeOperator(newOperator, { from: holder }); - expectEvent.inLogs(logs, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); - - (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); - }); - - it('non-operators can be authorized', async function () { - (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); - + it('can be re-authorized', async function () { const { logs } = await this.token.authorizeOperator(newOperator, { from: holder }); expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); (await this.token.isOperatorFor(newOperator, holder)).should.equal(true); }); - describe('new operators', function () { + it('can be revoked', async function () { + const { logs } = await this.token.revokeOperator(newOperator, { from: holder }); + expectEvent.inLogs(logs, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); + + (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); + }); + }); + + describe('default operators', function () { + it('can be re-authorized', async function () { + const { logs } = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); + expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); + + (await this.token.isOperatorFor(defaultOperatorA, holder)).should.equal(true); + }); + + it('can be revoked', async function () { + const { logs } = await this.token.revokeOperator(defaultOperatorA, { from: holder }); + expectEvent.inLogs(logs, 'RevokedOperator', { operator: defaultOperatorA, tokenHolder: holder }); + + (await this.token.isOperatorFor(defaultOperatorA, holder)).should.equal(false); + }); + + it('cannot be revoked for themselves', async function () { + await shouldFail.reverting(this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA })); + }); + + context('with revoked default operator', function () { beforeEach(async function () { - await this.token.authorizeOperator(newOperator, { from: holder }); + await this.token.revokeOperator(defaultOperatorA, { from: holder }); }); - it('are not added to the default operators list', async function () { + it('default operator is not revoked for other holders', async function () { + (await this.token.isOperatorFor(defaultOperatorA, anyone)).should.equal(true); + }); + + it('other default operators are not revoked', async function () { + (await this.token.isOperatorFor(defaultOperatorB, holder)).should.equal(true); + }); + + it('default operators list is not modified', async function () { (await this.token.defaultOperators()).should.deep.equal(defaultOperators); }); - it('can be re-authorized', async function () { - const { logs } = await this.token.authorizeOperator(newOperator, { from: holder }); - expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); - - (await this.token.isOperatorFor(newOperator, holder)).should.equal(true); - }); - - it('can be revoked', async function () { - const { logs } = await this.token.revokeOperator(newOperator, { from: holder }); - expectEvent.inLogs(logs, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); - - (await this.token.isOperatorFor(newOperator, holder)).should.equal(false); - }); - }); - - describe('default operators', function () { - it('can be re-authorized', async function () { + it('revoked default operator can be re-authorized', async function () { const { logs } = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); (await this.token.isOperatorFor(defaultOperatorA, holder)).should.equal(true); }); - - it('can be revoked', async function () { - const { logs } = await this.token.revokeOperator(defaultOperatorA, { from: holder }); - expectEvent.inLogs(logs, 'RevokedOperator', { operator: defaultOperatorA, tokenHolder: holder }); - - (await this.token.isOperatorFor(defaultOperatorA, holder)).should.equal(false); - }); - - it('cannot be revoked for themselves', async function () { - await shouldFail.reverting(this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA })); - }); - - context('with revoked default operator', function () { - beforeEach(async function () { - await this.token.revokeOperator(defaultOperatorA, { from: holder }); - }); - - it('default operator is not revoked for other holders', async function () { - (await this.token.isOperatorFor(defaultOperatorA, anyone)).should.equal(true); - }); - - it('other default operators are not revoked', async function () { - (await this.token.isOperatorFor(defaultOperatorB, holder)).should.equal(true); - }); - - it('default operators list is not modified', async function () { - (await this.token.defaultOperators()).should.deep.equal(defaultOperators); - }); - - it('revoked default operator can be re-authorized', async function () { - const { logs } = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); - expectEvent.inLogs(logs, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); - - (await this.token.isOperatorFor(defaultOperatorA, holder)).should.equal(true); - }); - }); }); }); + }); - describe('send and receive hooks', function () { - const amount = new BN('1'); - const operator = defaultOperatorA; - // sender and recipient are stored inside 'this', since in some tests their addresses are determined dynamically + describe('send and receive hooks', function () { + const amount = new BN('1'); + const operator = defaultOperatorA; + // sender and recipient are stored inside 'this', since in some tests their addresses are determined dynamically - describe('tokensReceived', function () { - beforeEach(function () { - this.sender = holder; - }); - - context('with no ERC777TokensRecipient implementer', function () { - context('with contract recipient', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = this.tokensRecipientImplementer.address; - - // Note that tokensRecipientImplementer doesn't implement the recipient interface for the recipient - }); - - it('send reverts', async function () { - await shouldFail.reverting(this.token.send(this.recipient, amount, data)); - }); - - it('operatorSend reverts', async function () { - await shouldFail.reverting( - this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }) - ); - }); - - it('mint (internal) reverts', async function () { - await shouldFail.reverting( - this.token.mintInternal(operator, this.recipient, amount, data, operatorData) - ); - }); - }); - }); - - context('with ERC777TokensRecipient implementer', function () { - context('with contract as implementer for an externally owned account', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = anyone; - - await this.tokensRecipientImplementer.recipientFor(this.recipient); - - await this.erc1820.setInterfaceImplementer( - this.recipient, - web3.utils.soliditySha3('ERC777TokensRecipient'), this.tokensRecipientImplementer.address, - { from: this.recipient }, - ); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - - context('with contract as implementer for another contract', function () { - beforeEach(async function () { - this.recipientContract = await ERC777SenderRecipientMock.new(); - this.recipient = this.recipientContract.address; - - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - await this.tokensRecipientImplementer.recipientFor(this.recipient); - await this.recipientContract.registerRecipient(this.tokensRecipientImplementer.address); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - - context('with contract as implementer for itself', function () { - beforeEach(async function () { - this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); - this.recipient = this.tokensRecipientImplementer.address; - - await this.tokensRecipientImplementer.recipientFor(this.recipient); - }); - - shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); - }); - }); + describe('tokensReceived', function () { + beforeEach(function () { + this.sender = holder; }); - describe('tokensToSend', function () { - beforeEach(function () { - this.recipient = anyone; - }); - - context('with a contract as implementer for an externally owned account', function () { + context('with no ERC777TokensRecipient implementer', function () { + context('with contract recipient', function () { beforeEach(async function () { - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - this.sender = holder; + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = this.tokensRecipientImplementer.address; - await this.tokensSenderImplementer.senderFor(this.sender); + // Note that tokensRecipientImplementer doesn't implement the recipient interface for the recipient + }); - await this.erc1820.setInterfaceImplementer( - this.sender, - web3.utils.soliditySha3('ERC777TokensSender'), this.tokensSenderImplementer.address, - { from: this.sender }, + it('send reverts', async function () { + await shouldFail.reverting(this.token.send(this.recipient, amount, data)); + }); + + it('operatorSend reverts', async function () { + await shouldFail.reverting( + this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }) ); }); - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + it('mint (internal) reverts', async function () { + await shouldFail.reverting( + this.token.mintInternal(operator, this.recipient, amount, data, operatorData) + ); + }); + }); + }); + + context('with ERC777TokensRecipient implementer', function () { + context('with contract as implementer for an externally owned account', function () { + beforeEach(async function () { + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = anyone; + + await this.tokensRecipientImplementer.recipientFor(this.recipient); + + await this.erc1820.setInterfaceImplementer( + this.recipient, + web3.utils.soliditySha3('ERC777TokensRecipient'), this.tokensRecipientImplementer.address, + { from: this.recipient }, + ); + }); + + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); }); context('with contract as implementer for another contract', function () { beforeEach(async function () { - this.senderContract = await ERC777SenderRecipientMock.new(); - this.sender = this.senderContract.address; + this.recipientContract = await ERC777SenderRecipientMock.new(); + this.recipient = this.recipientContract.address; - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - await this.tokensSenderImplementer.senderFor(this.sender); - await this.senderContract.registerSender(this.tokensSenderImplementer.address); - - // For the contract to be able to receive tokens (that it can later send), it must also implement the - // recipient interface. - - await this.senderContract.recipientFor(this.sender); - await this.token.send(this.sender, amount, data, { from: holder }); + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + await this.tokensRecipientImplementer.recipientFor(this.recipient); + await this.recipientContract.registerRecipient(this.tokensRecipientImplementer.address); }); - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); }); - context('with a contract as implementer for itself', function () { + context('with contract as implementer for itself', function () { beforeEach(async function () { - this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); - this.sender = this.tokensSenderImplementer.address; + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = this.tokensRecipientImplementer.address; - await this.tokensSenderImplementer.senderFor(this.sender); - - // For the contract to be able to receive tokens (that it can later send), it must also implement the - // recipient interface. - - await this.tokensSenderImplementer.recipientFor(this.sender); - await this.token.send(this.sender, amount, data, { from: holder }); + await this.tokensRecipientImplementer.recipientFor(this.recipient); }); - shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); }); }); }); - }); - context('with no default operators', function () { - beforeEach(async function () { - this.token = await ERC777.new(holder, initialSupply, name, symbol, granularity, []); - }); + describe('tokensToSend', function () { + beforeEach(function () { + this.recipient = anyone; + }); - it('default operators list is empty', async function () { - (await this.token.defaultOperators()).should.deep.equal([]); + context('with a contract as implementer for an externally owned account', function () { + beforeEach(async function () { + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + this.sender = holder; + + await this.tokensSenderImplementer.senderFor(this.sender); + + await this.erc1820.setInterfaceImplementer( + this.sender, + web3.utils.soliditySha3('ERC777TokensSender'), this.tokensSenderImplementer.address, + { from: this.sender }, + ); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); + + context('with contract as implementer for another contract', function () { + beforeEach(async function () { + this.senderContract = await ERC777SenderRecipientMock.new(); + this.sender = this.senderContract.address; + + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + await this.tokensSenderImplementer.senderFor(this.sender); + await this.senderContract.registerSender(this.tokensSenderImplementer.address); + + // For the contract to be able to receive tokens (that it can later send), it must also implement the + // recipient interface. + + await this.senderContract.recipientFor(this.sender); + await this.token.send(this.sender, amount, data, { from: holder }); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); + + context('with a contract as implementer for itself', function () { + beforeEach(async function () { + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + this.sender = this.tokensSenderImplementer.address; + + await this.tokensSenderImplementer.senderFor(this.sender); + + // For the contract to be able to receive tokens (that it can later send), it must also implement the + // recipient interface. + + await this.tokensSenderImplementer.recipientFor(this.sender); + await this.token.send(this.sender, amount, data, { from: holder }); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); }); }); }); - context('with granularity larger than 1', function () { - const granularity = new BN('4'); - + context('with no default operators', function () { beforeEach(async function () { - initialSupply.mod(granularity).should.be.bignumber.equal('0'); - - this.token = await ERC777.new(holder, initialSupply, name, symbol, granularity, defaultOperators); + this.token = await ERC777.new(holder, initialSupply, name, symbol, []); }); - it('returns the granularity', async function () { - (await this.token.granularity()).should.be.bignumber.equal(granularity); - }); - - context('when the sender has tokens', function () { - const from = holder; - - shouldDirectSendTokens(from, anyone, new BN('0'), data); - shouldDirectSendTokens(from, anyone, granularity, data); - shouldDirectSendTokens(from, anyone, granularity.muln(2), data); - - it('reverts when sending an amount non-multiple of the granularity', async function () { - await shouldFail.reverting(this.token.send(anyone, granularity.subn(1), data, { from })); - }); - - shouldDirectBurnTokens(from, new BN('0'), data); - shouldDirectBurnTokens(from, granularity, data); - shouldDirectBurnTokens(from, granularity.muln(2), data); - - it('reverts when burning an amount non-multiple of the granularity', async function () { - await shouldFail.reverting(this.token.burn(granularity.subn(1), data, { from })); - }); - }); - - shouldInternalMintTokens(anyone, defaultOperatorA, new BN('0'), data, operatorData); - shouldInternalMintTokens(anyone, defaultOperatorA, granularity, data, operatorData); - shouldInternalMintTokens(anyone, defaultOperatorA, granularity.muln(2), data, operatorData); - - it('reverts when minting an amount non-multiple of the granularity', async function () { - await shouldFail.reverting( - this.token.mintInternal(defaultOperatorA, anyone, granularity.subn(1), data, operatorData) - ); + it('default operators list is empty', async function () { + (await this.token.defaultOperators()).should.deep.equal([]); }); }); });