Unverified Commit 0408e51a by Nicolás Venturo Committed by GitHub

Bundle ERC20Detailed (#2161)

* Merge ERC20Detailed into ERC20, make derived contracts abstract

* Fix Create2 tests

* Fix failing test

* Default decimals to 18

* Add tests for setupDecimals

* Add changelog entry

* Update CHANGELOG.md

* Update CHANGELOG.md

* Replace isConstructor for !isContract

* Update CHANGELOG.md

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

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
parent 5b5d91c9
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
* `ERC777`: removed `_callsTokensToSend` and `_callTokensReceived`. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134)) * `ERC777`: removed `_callsTokensToSend` and `_callTokensReceived`. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134))
* `EnumerableSet`: renamed `get` to `at`. ([#2151](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2151)) * `EnumerableSet`: renamed `get` to `at`. ([#2151](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2151))
* `ERC165Checker`: functions no longer have a leading underscore. ([#2150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2150)) * `ERC165Checker`: functions no longer have a leading underscore. ([#2150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2150))
* `ERC20Detailed`: this contract was removed and its functionality merged into `ERC20`. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161))
* `ERC20`: added a constructor for `name` and `symbol`. `decimals` now defaults to 18. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161))
## 2.5.0 (2020-02-04) ## 2.5.0 (2020-02-04)
......
...@@ -5,7 +5,6 @@ import "../math/SafeMath.sol"; ...@@ -5,7 +5,6 @@ import "../math/SafeMath.sol";
import "../access/Ownable.sol"; import "../access/Ownable.sol";
import "../token/ERC20/SafeERC20.sol"; import "../token/ERC20/SafeERC20.sol";
import "../token/ERC20/ERC20.sol"; import "../token/ERC20/ERC20.sol";
import "../token/ERC20/ERC20Detailed.sol";
/** /**
* @dev A xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategy] that charges transaction fees in a special purpose ERC20 * @dev A xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategy] that charges transaction fees in a special purpose ERC20
...@@ -30,7 +29,7 @@ contract GSNRecipientERC20Fee is GSNRecipient { ...@@ -30,7 +29,7 @@ contract GSNRecipientERC20Fee is GSNRecipient {
* @dev The arguments to the constructor are the details that the gas payment token will have: `name` and `symbol`. `decimals` is hard-coded to 18. * @dev The arguments to the constructor are the details that the gas payment token will have: `name` and `symbol`. `decimals` is hard-coded to 18.
*/ */
constructor(string memory name, string memory symbol) public { constructor(string memory name, string memory symbol) public {
_token = new __unstable__ERC20Owned(name, symbol, 18); _token = new __unstable__ERC20Owned(name, symbol);
} }
/** /**
...@@ -112,10 +111,10 @@ contract GSNRecipientERC20Fee is GSNRecipient { ...@@ -112,10 +111,10 @@ contract GSNRecipientERC20Fee is GSNRecipient {
* outside of this context. * outside of this context.
*/ */
// solhint-disable-next-line contract-name-camelcase // solhint-disable-next-line contract-name-camelcase
contract __unstable__ERC20Owned is ERC20, ERC20Detailed, Ownable { contract __unstable__ERC20Owned is ERC20, Ownable {
uint256 private constant _UINT256_MAX = 2**256 - 1; uint256 private constant _UINT256_MAX = 2**256 - 1;
constructor(string memory name, string memory symbol, uint8 decimals) public ERC20Detailed(name, symbol, decimals) { } constructor(string memory name, string memory symbol) public ERC20(name, symbol) { }
// The owner (GSNRecipientERC20Fee) can mint tokens // The owner (GSNRecipientERC20Fee) can mint tokens
function mint(address account, uint256 amount) public onlyOwner { function mint(address account, uint256 amount) public onlyOwner {
...@@ -123,7 +122,7 @@ contract __unstable__ERC20Owned is ERC20, ERC20Detailed, Ownable { ...@@ -123,7 +122,7 @@ contract __unstable__ERC20Owned is ERC20, ERC20Detailed, Ownable {
} }
// The owner has 'infinite' allowance for all token holders // The owner has 'infinite' allowance for all token holders
function allowance(address tokenOwner, address spender) public view override(ERC20, IERC20) returns (uint256) { function allowance(address tokenOwner, address spender) public view override returns (uint256) {
if (spender == owner()) { if (spender == owner()) {
return _UINT256_MAX; return _UINT256_MAX;
} else { } else {
...@@ -140,7 +139,7 @@ contract __unstable__ERC20Owned is ERC20, ERC20Detailed, Ownable { ...@@ -140,7 +139,7 @@ contract __unstable__ERC20Owned is ERC20, ERC20Detailed, Ownable {
} }
} }
function transferFrom(address sender, address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) { function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
if (recipient == owner()) { if (recipient == owner()) {
_transfer(sender, recipient, amount); _transfer(sender, recipient, amount);
return true; return true;
......
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
import "../utils/Create2.sol"; import "../utils/Create2.sol";
import "../token/ERC20/ERC20.sol"; import "../introspection/ERC1820Implementer.sol";
contract Create2Impl { contract Create2Impl {
function deploy(uint256 value, bytes32 salt, bytes memory code) public { function deploy(uint256 value, bytes32 salt, bytes memory code) public {
Create2.deploy(value, salt, code); Create2.deploy(value, salt, code);
} }
function deployERC20(uint256 value, bytes32 salt) public { function deployERC1820Implementer(uint256 value, bytes32 salt) public {
// solhint-disable-next-line indent // solhint-disable-next-line indent
Create2.deploy(value, salt, type(ERC20).creationCode); Create2.deploy(value, salt, type(ERC1820Implementer).creationCode);
} }
function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) { function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) {
......
...@@ -3,7 +3,12 @@ pragma solidity ^0.6.0; ...@@ -3,7 +3,12 @@ pragma solidity ^0.6.0;
import "../token/ERC20/ERC20Burnable.sol"; import "../token/ERC20/ERC20Burnable.sol";
contract ERC20BurnableMock is ERC20Burnable { contract ERC20BurnableMock is ERC20Burnable {
constructor (address initialAccount, uint256 initialBalance) public { constructor (
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
_mint(initialAccount, initialBalance); _mint(initialAccount, initialBalance);
} }
} }
...@@ -3,7 +3,9 @@ pragma solidity ^0.6.0; ...@@ -3,7 +3,9 @@ pragma solidity ^0.6.0;
import "../token/ERC20/ERC20Capped.sol"; import "../token/ERC20/ERC20Capped.sol";
contract ERC20CappedMock is ERC20Capped { contract ERC20CappedMock is ERC20Capped {
constructor (uint256 cap) public ERC20Capped(cap) { } constructor (string memory name, string memory symbol, uint256 cap)
public ERC20(name, symbol) ERC20Capped(cap)
{ }
function mint(address to, uint256 tokenId) public { function mint(address to, uint256 tokenId) public {
_mint(to, tokenId); _mint(to, tokenId);
......
pragma solidity ^0.6.0;
import "../token/ERC20/ERC20.sol";
contract ERC20DecimalsMock is ERC20 {
constructor (string memory name, string memory symbol, uint8 decimals) public ERC20(name, symbol) {
_setupDecimals(decimals);
}
function setupDecimals(uint8 decimals) public {
_setupDecimals(decimals);
}
}
pragma solidity ^0.6.0;
import "../token/ERC20/ERC20.sol";
import "../token/ERC20/ERC20Detailed.sol";
contract ERC20DetailedMock is ERC20, ERC20Detailed {
constructor (string memory name, string memory symbol, uint8 decimals)
public
ERC20Detailed(name, symbol, decimals)
{
}
}
...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20.sol"; ...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20.sol";
// mock class using ERC20 // mock class using ERC20
contract ERC20Mock is ERC20 { contract ERC20Mock is ERC20 {
constructor (address initialAccount, uint256 initialBalance) public payable { constructor (
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable ERC20(name, symbol) {
_mint(initialAccount, initialBalance); _mint(initialAccount, initialBalance);
} }
......
...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20Pausable.sol"; ...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20Pausable.sol";
// mock class using ERC20Pausable // mock class using ERC20Pausable
contract ERC20PausableMock is ERC20Pausable { contract ERC20PausableMock is ERC20Pausable {
constructor (address initialAccount, uint256 initialBalance) public { constructor (
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
_mint(initialAccount, initialBalance); _mint(initialAccount, initialBalance);
} }
......
...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20Snapshot.sol"; ...@@ -4,7 +4,12 @@ import "../token/ERC20/ERC20Snapshot.sol";
contract ERC20SnapshotMock is ERC20Snapshot { contract ERC20SnapshotMock is ERC20Snapshot {
constructor(address initialAccount, uint256 initialBalance) public { constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
_mint(initialAccount, initialBalance); _mint(initialAccount, initialBalance);
} }
......
...@@ -3,6 +3,7 @@ pragma solidity ^0.6.0; ...@@ -3,6 +3,7 @@ pragma solidity ^0.6.0;
import "../../GSN/Context.sol"; import "../../GSN/Context.sol";
import "./IERC20.sol"; import "./IERC20.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/** /**
* @dev Implementation of the {IERC20} interface. * @dev Implementation of the {IERC20} interface.
...@@ -30,6 +31,7 @@ import "../../math/SafeMath.sol"; ...@@ -30,6 +31,7 @@ import "../../math/SafeMath.sol";
*/ */
contract ERC20 is Context, IERC20 { contract ERC20 is Context, IERC20 {
using SafeMath for uint256; using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances; mapping (address => uint256) private _balances;
...@@ -37,6 +39,57 @@ contract ERC20 is Context, IERC20 { ...@@ -37,6 +39,57 @@ contract ERC20 is Context, IERC20 {
uint256 private _totalSupply; uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/** /**
* @dev See {IERC20-totalSupply}. * @dev See {IERC20-totalSupply}.
*/ */
...@@ -224,6 +277,18 @@ contract ERC20 is Context, IERC20 { ...@@ -224,6 +277,18 @@ contract ERC20 is Context, IERC20 {
} }
/** /**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* Requirements:
*
* - this function can only be called from a constructor.
*/
function _setupDecimals(uint8 decimals_) internal {
require(!address(this).isContract(), "ERC20: decimals cannot be changed after construction");
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes * @dev Hook that is called before any transfer of tokens. This includes
* minting and burning. * minting and burning.
* *
......
...@@ -8,7 +8,7 @@ import "./ERC20.sol"; ...@@ -8,7 +8,7 @@ import "./ERC20.sol";
* tokens and those that they have an allowance for, in a way that can be * tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis). * recognized off-chain (via event analysis).
*/ */
contract ERC20Burnable is Context, ERC20 { abstract contract ERC20Burnable is Context, ERC20 {
/** /**
* @dev Destroys `amount` tokens from the caller. * @dev Destroys `amount` tokens from the caller.
* *
......
...@@ -5,7 +5,7 @@ import "./ERC20.sol"; ...@@ -5,7 +5,7 @@ import "./ERC20.sol";
/** /**
* @dev Extension of {ERC20} that adds a cap to the supply of tokens. * @dev Extension of {ERC20} that adds a cap to the supply of tokens.
*/ */
contract ERC20Capped is ERC20 { abstract contract ERC20Capped is ERC20 {
uint256 private _cap; uint256 private _cap;
/** /**
......
pragma solidity ^0.6.0;
import "./IERC20.sol";
/**
* @dev Optional functions from the ERC20 standard.
*/
abstract contract ERC20Detailed is IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
* these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
}
...@@ -11,7 +11,7 @@ import "../../utils/Pausable.sol"; ...@@ -11,7 +11,7 @@ import "../../utils/Pausable.sol";
* period, or having an emergency switch for freezing all token transfers in the * period, or having an emergency switch for freezing all token transfers in the
* event of a large bug. * event of a large bug.
*/ */
contract ERC20Pausable is ERC20, Pausable { abstract contract ERC20Pausable is ERC20, Pausable {
/** /**
* @dev See {ERC20-_beforeTokenTransfer}. * @dev See {ERC20-_beforeTokenTransfer}.
* *
......
...@@ -17,7 +17,7 @@ import "./ERC20.sol"; ...@@ -17,7 +17,7 @@ import "./ERC20.sol";
* account address. * account address.
* @author Validity Labs AG <info@validitylabs.org> * @author Validity Labs AG <info@validitylabs.org>
*/ */
contract ERC20Snapshot is ERC20 { abstract contract ERC20Snapshot is ERC20 {
// Inspired by Jordi Baylina's MiniMeToken to record historical balances: // Inspired by Jordi Baylina's MiniMeToken to record historical balances:
// https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol
......
...@@ -6,7 +6,7 @@ const gsn = require('@openzeppelin/gsn-helpers'); ...@@ -6,7 +6,7 @@ const gsn = require('@openzeppelin/gsn-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const GSNRecipientERC20FeeMock = contract.fromArtifact('GSNRecipientERC20FeeMock'); const GSNRecipientERC20FeeMock = contract.fromArtifact('GSNRecipientERC20FeeMock');
const ERC20Detailed = contract.fromArtifact('ERC20Detailed'); const ERC20 = contract.fromArtifact('ERC20');
const IRelayHub = contract.fromArtifact('IRelayHub'); const IRelayHub = contract.fromArtifact('IRelayHub');
describe('GSNRecipientERC20Fee', function () { describe('GSNRecipientERC20Fee', function () {
...@@ -17,7 +17,7 @@ describe('GSNRecipientERC20Fee', function () { ...@@ -17,7 +17,7 @@ describe('GSNRecipientERC20Fee', function () {
beforeEach(async function () { beforeEach(async function () {
this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol); this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol);
this.token = await ERC20Detailed.at(await this.recipient.token()); this.token = await ERC20.at(await this.recipient.token());
}); });
describe('token', function () { describe('token', function () {
......
...@@ -11,14 +11,45 @@ const { ...@@ -11,14 +11,45 @@ const {
} = require('./ERC20.behavior'); } = require('./ERC20.behavior');
const ERC20Mock = contract.fromArtifact('ERC20Mock'); const ERC20Mock = contract.fromArtifact('ERC20Mock');
const ERC20DecimalsMock = contract.fromArtifact('ERC20DecimalsMock');
describe('ERC20', function () { describe('ERC20', function () {
const [ initialHolder, recipient, anotherAccount ] = accounts; const [ initialHolder, recipient, anotherAccount ] = accounts;
const name = 'My Token';
const symbol = 'MTKN';
const initialSupply = new BN(100); const initialSupply = new BN(100);
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20Mock.new(initialHolder, initialSupply); this.token = await ERC20Mock.new(name, symbol, initialHolder, initialSupply);
});
it('has a name', async function () {
expect(await this.token.name()).to.equal(name);
});
it('has a symbol', async function () {
expect(await this.token.symbol()).to.equal(symbol);
});
it('has 18 decimals', async function () {
expect(await this.token.decimals()).to.be.bignumber.equal('18');
});
describe('_setupDecimals', function () {
const decimals = new BN(6);
it('can set decimals during construction', async function () {
const token = await ERC20DecimalsMock.new(name, symbol, decimals);
expect(await token.decimals()).to.be.bignumber.equal(decimals);
});
it('reverts if setting decimals after construction', async function () {
const token = await ERC20DecimalsMock.new(name, symbol, decimals);
await expectRevert(token.setupDecimals(decimals.addn(1)), 'ERC20: decimals cannot be changed after construction');
});
}); });
shouldBehaveLikeERC20('ERC20', initialSupply, initialHolder, recipient, anotherAccount); shouldBehaveLikeERC20('ERC20', initialSupply, initialHolder, recipient, anotherAccount);
......
...@@ -10,8 +10,11 @@ describe('ERC20Burnable', function () { ...@@ -10,8 +10,11 @@ describe('ERC20Burnable', function () {
const initialBalance = new BN(1000); const initialBalance = new BN(1000);
const name = 'My Token';
const symbol = 'MTKN';
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20BurnableMock.new(owner, initialBalance, { from: owner }); this.token = await ERC20BurnableMock.new(name, symbol, owner, initialBalance, { from: owner });
}); });
shouldBehaveLikeERC20Burnable(owner, initialBalance, otherAccounts); shouldBehaveLikeERC20Burnable(owner, initialBalance, otherAccounts);
......
...@@ -10,15 +10,18 @@ describe('ERC20Capped', function () { ...@@ -10,15 +10,18 @@ describe('ERC20Capped', function () {
const cap = ether('1000'); const cap = ether('1000');
const name = 'My Token';
const symbol = 'MTKN';
it('requires a non-zero cap', async function () { it('requires a non-zero cap', async function () {
await expectRevert( await expectRevert(
ERC20Capped.new(new BN(0), { from: minter }), 'ERC20Capped: cap is 0' ERC20Capped.new(name, symbol, new BN(0), { from: minter }), 'ERC20Capped: cap is 0'
); );
}); });
context('once deployed', async function () { context('once deployed', async function () {
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20Capped.new(cap, { from: minter }); this.token = await ERC20Capped.new(name, symbol, cap, { from: minter });
}); });
shouldBehaveLikeERC20Capped(minter, otherAccounts, cap); shouldBehaveLikeERC20Capped(minter, otherAccounts, cap);
......
const { contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC20DetailedMock = contract.fromArtifact('ERC20DetailedMock');
describe('ERC20Detailed', function () {
const _name = 'My Detailed ERC20';
const _symbol = 'MDT';
const _decimals = new BN(18);
beforeEach(async function () {
this.detailedERC20 = await ERC20DetailedMock.new(_name, _symbol, _decimals);
});
it('has a name', async function () {
expect(await this.detailedERC20.name()).to.equal(_name);
});
it('has a symbol', async function () {
expect(await this.detailedERC20.symbol()).to.equal(_symbol);
});
it('has an amount of decimals', async function () {
expect(await this.detailedERC20.decimals()).to.be.bignumber.equal(_decimals);
});
});
...@@ -11,8 +11,11 @@ describe('ERC20Pausable', function () { ...@@ -11,8 +11,11 @@ describe('ERC20Pausable', function () {
const initialSupply = new BN(100); const initialSupply = new BN(100);
const name = 'My Token';
const symbol = 'MTKN';
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20PausableMock.new(holder, initialSupply); this.token = await ERC20PausableMock.new(name, symbol, holder, initialSupply);
}); });
describe('pausable token', function () { describe('pausable token', function () {
......
...@@ -10,8 +10,11 @@ describe('ERC20Snapshot', function () { ...@@ -10,8 +10,11 @@ describe('ERC20Snapshot', function () {
const initialSupply = new BN(100); const initialSupply = new BN(100);
const name = 'My Token';
const symbol = 'MTKN';
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20SnapshotMock.new(initialHolder, initialSupply); this.token = await ERC20SnapshotMock.new(name, symbol, initialHolder, initialSupply);
}); });
describe('snapshot', function () { describe('snapshot', function () {
......
...@@ -10,11 +10,14 @@ const TokenTimelock = contract.fromArtifact('TokenTimelock'); ...@@ -10,11 +10,14 @@ const TokenTimelock = contract.fromArtifact('TokenTimelock');
describe('TokenTimelock', function () { describe('TokenTimelock', function () {
const [ beneficiary ] = accounts; const [ beneficiary ] = accounts;
const name = 'My Token';
const symbol = 'MTKN';
const amount = new BN(100); const amount = new BN(100);
context('with token', function () { context('with token', function () {
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC20Mock.new(beneficiary, 0); // We're not using the preminted tokens this.token = await ERC20Mock.new(name, symbol, beneficiary, 0); // We're not using the preminted tokens
}); });
it('rejects a release time in the past', async function () { it('rejects a release time in the past', async function () {
......
...@@ -5,16 +5,20 @@ const { expect } = require('chai'); ...@@ -5,16 +5,20 @@ const { expect } = require('chai');
const Create2Impl = contract.fromArtifact('Create2Impl'); const Create2Impl = contract.fromArtifact('Create2Impl');
const ERC20Mock = contract.fromArtifact('ERC20Mock'); const ERC20Mock = contract.fromArtifact('ERC20Mock');
const ERC20 = contract.fromArtifact('ERC20'); const ERC1820Implementer = contract.fromArtifact('ERC1820Implementer');
describe('Create2', function () { describe('Create2', function () {
const [deployerAccount] = accounts; const [deployerAccount] = accounts;
const salt = 'salt message'; const salt = 'salt message';
const saltHex = web3.utils.soliditySha3(salt); const saltHex = web3.utils.soliditySha3(salt);
const constructorByteCode = `${ERC20Mock.bytecode}${web3.eth.abi
.encodeParameters(['address', 'uint256'], [deployerAccount, 100]).slice(2) const encodedParams = web3.eth.abi.encodeParameters(
}`; ['string', 'string', 'address', 'uint256'],
['MyToken', 'MTKN', deployerAccount, 100]
).slice(2);
const constructorByteCode = `${ERC20Mock.bytecode}${encodedParams}`;
beforeEach(async function () { beforeEach(async function () {
this.factory = await Create2Impl.new(); this.factory = await Create2Impl.new();
...@@ -36,19 +40,20 @@ describe('Create2', function () { ...@@ -36,19 +40,20 @@ describe('Create2', function () {
expect(onChainComputed).to.equal(offChainComputed); expect(onChainComputed).to.equal(offChainComputed);
}); });
it('should deploy a ERC20 from inline assembly code', async function () { it('should deploy a ERC1820Implementer from inline assembly code', async function () {
const offChainComputed = const offChainComputed =
computeCreate2Address(saltHex, ERC20.bytecode, this.factory.address); computeCreate2Address(saltHex, ERC1820Implementer.bytecode, this.factory.address);
await this.factory
.deploy(0, saltHex, ERC20.bytecode, { from: deployerAccount }); await this.factory.deployERC1820Implementer(0, saltHex);
expect(ERC20.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2));
expect(ERC1820Implementer.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2));
}); });
it('should deploy a ERC20Mock with correct balances', async function () { it('should deploy a ERC20Mock with correct balances', async function () {
const offChainComputed = const offChainComputed = computeCreate2Address(saltHex, constructorByteCode, this.factory.address);
computeCreate2Address(saltHex, constructorByteCode, this.factory.address);
await this.factory await this.factory.deploy(0, saltHex, constructorByteCode);
.deploy(0, saltHex, constructorByteCode, { from: deployerAccount });
const erc20 = await ERC20Mock.at(offChainComputed); const erc20 = await ERC20Mock.at(offChainComputed);
expect(await erc20.balanceOf(deployerAccount)).to.be.bignumber.equal(new BN(100)); expect(await erc20.balanceOf(deployerAccount)).to.be.bignumber.equal(new BN(100));
}); });
......
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