Commit 3682c657 by Balaji Pachai Committed by Nicolás Venturo

Added message string for require() (#1704)

* Error handling in ERC20 and ERC721

* Added message string for require.

* Fixed solhint errors.

* Updated PR as per issue #1709

* changes as per #1709 and openzeppelin forum.

* Changes in require statement

* Changes in require statement

* build pipeline fix

* Changes as per @nventuro's comment.

* Update revert reason strings.

* Fianal update of revert reason strings.

* WIP: Updating reason strings in test cases

* WIP: Added changes to ERC20 and ERC721

* Fixes linting errors in *.tes.js files

* Achieved 100% code coverage

* Updated the test cases with shouldFail.reverting.withMessage()

* Fix package-lock.

* address review comments

* fix linter issues

* fix remaining revert reasons
parent 4a0a67b0
......@@ -13,8 +13,7 @@ library Roles {
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account));
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
......@@ -22,8 +21,7 @@ library Roles {
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account));
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
......@@ -32,7 +30,7 @@ library Roles {
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0));
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
......@@ -15,7 +15,7 @@ contract CapperRole {
}
modifier onlyCapper() {
require(isCapper(msg.sender));
require(isCapper(msg.sender), "CapperRole: caller does not have the Capper role");
_;
}
......
......@@ -15,7 +15,7 @@ contract MinterRole {
}
modifier onlyMinter() {
require(isMinter(msg.sender));
require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role");
_;
}
......
......@@ -15,7 +15,7 @@ contract PauserRole {
}
modifier onlyPauser() {
require(isPauser(msg.sender));
require(isPauser(msg.sender), "PauserRole: caller does not have the Pauser role");
_;
}
......
......@@ -15,7 +15,7 @@ contract SignerRole {
}
modifier onlySigner() {
require(isSigner(msg.sender));
require(isSigner(msg.sender), "SignerRole: caller does not have the Signer role");
_;
}
......
......@@ -19,7 +19,7 @@ contract WhitelistAdminRole {
}
modifier onlyWhitelistAdmin() {
require(isWhitelistAdmin(msg.sender));
require(isWhitelistAdmin(msg.sender), "WhitelistAdminRole: caller does not have the WhitelistAdmin role");
_;
}
......
......@@ -18,7 +18,7 @@ contract WhitelistedRole is WhitelistAdminRole {
Roles.Role private _whitelisteds;
modifier onlyWhitelisted() {
require(isWhitelisted(msg.sender));
require(isWhitelisted(msg.sender), "WhitelistedRole: caller does not have the Whitelisted role");
_;
}
......
......@@ -54,9 +54,9 @@ contract Crowdsale is ReentrancyGuard {
* @param token Address of the token being sold
*/
constructor (uint256 rate, address payable wallet, IERC20 token) public {
require(rate > 0);
require(wallet != address(0));
require(address(token) != address(0));
require(rate > 0, "Crowdsale: rate is 0");
require(wallet != address(0), "Crowdsale: wallet is the zero address");
require(address(token) != address(0), "Crowdsale: token is the zero address");
_rate = rate;
_wallet = wallet;
......@@ -136,8 +136,8 @@ contract Crowdsale is ReentrancyGuard {
* @param weiAmount Value in wei involved in the purchase
*/
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
require(beneficiary != address(0));
require(weiAmount != 0);
require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address");
require(weiAmount != 0, "Crowdsale: weiAmount is 0");
}
/**
......
......@@ -31,8 +31,8 @@ contract FinalizableCrowdsale is TimedCrowdsale {
* work. Calls the contract's finalization function.
*/
function finalize() public {
require(!_finalized);
require(hasClosed());
require(!_finalized, "FinalizableCrowdsale: already finalized");
require(hasClosed(), "FinalizableCrowdsale: not closed");
_finalized = true;
......
......@@ -24,9 +24,9 @@ contract PostDeliveryCrowdsale is TimedCrowdsale {
* @param beneficiary Whose tokens will be withdrawn.
*/
function withdrawTokens(address beneficiary) public {
require(hasClosed());
require(hasClosed(), "PostDeliveryCrowdsale: not closed");
uint256 amount = _balances[beneficiary];
require(amount > 0);
require(amount > 0, "PostDeliveryCrowdsale: beneficiary is not due any tokens");
_balances[beneficiary] = 0;
_vault.transfer(token(), beneficiary, amount);
......
......@@ -28,7 +28,7 @@ contract RefundableCrowdsale is FinalizableCrowdsale {
* @param goal Funding goal
*/
constructor (uint256 goal) public {
require(goal > 0);
require(goal > 0, "RefundableCrowdsale: goal is 0");
_escrow = new RefundEscrow(wallet());
_goal = goal;
}
......@@ -45,8 +45,8 @@ contract RefundableCrowdsale is FinalizableCrowdsale {
* @param refundee Whose refund will be claimed.
*/
function claimRefund(address payable refundee) public {
require(finalized());
require(!goalReached());
require(finalized(), "RefundableCrowdsale: not finalized");
require(!goalReached(), "RefundableCrowdsale: goal reached");
_escrow.withdraw(refundee);
}
......
......@@ -12,8 +12,8 @@ import "./PostDeliveryCrowdsale.sol";
*/
contract RefundablePostDeliveryCrowdsale is RefundableCrowdsale, PostDeliveryCrowdsale {
function withdrawTokens(address beneficiary) public {
require(finalized());
require(goalReached());
require(finalized(), "RefundablePostDeliveryCrowdsale: not finalized");
require(goalReached(), "RefundablePostDeliveryCrowdsale: goal not reached");
super.withdrawTokens(beneficiary);
}
......
......@@ -21,7 +21,7 @@ contract AllowanceCrowdsale is Crowdsale {
* @param tokenWallet Address holding the tokens, which has approved allowance to the crowdsale.
*/
constructor (address tokenWallet) public {
require(tokenWallet != address(0));
require(tokenWallet != address(0), "AllowanceCrowdsale: token wallet is the zero address");
_tokenWallet = tokenWallet;
}
......
......@@ -16,6 +16,9 @@ contract MintedCrowdsale is Crowdsale {
*/
function _deliverTokens(address beneficiary, uint256 tokenAmount) internal {
// Potentially dangerous assumption about the type of the token.
require(ERC20Mintable(address(token())).mint(beneficiary, tokenAmount));
require(
ERC20Mintable(address(token())).mint(beneficiary, tokenAmount),
"MintedCrowdsale: minting failed"
);
}
}
......@@ -21,8 +21,9 @@ contract IncreasingPriceCrowdsale is TimedCrowdsale {
* @param finalRate Number of tokens a buyer gets per wei at the end of the crowdsale
*/
constructor (uint256 initialRate, uint256 finalRate) public {
require(finalRate > 0);
require(initialRate > finalRate);
require(finalRate > 0, "IncreasingPriceCrowdsale: final rate is 0");
// solhint-disable-next-line max-line-length
require(initialRate > finalRate, "IncreasingPriceCrowdsale: initial rate is not greater than final rate");
_initialRate = initialRate;
_finalRate = finalRate;
}
......@@ -32,7 +33,7 @@ contract IncreasingPriceCrowdsale is TimedCrowdsale {
* all calls to it are a mistake.
*/
function rate() public view returns (uint256) {
revert();
revert("IncreasingPriceCrowdsale: rate() called");
}
/**
......
......@@ -17,7 +17,7 @@ contract CappedCrowdsale is Crowdsale {
* @param cap Max amount of wei to be contributed
*/
constructor (uint256 cap) public {
require(cap > 0);
require(cap > 0, "CappedCrowdsale: cap is 0");
_cap = cap;
}
......@@ -43,6 +43,6 @@ contract CappedCrowdsale is Crowdsale {
*/
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
super._preValidatePurchase(beneficiary, weiAmount);
require(weiRaised().add(weiAmount) <= _cap);
require(weiRaised().add(weiAmount) <= _cap, "CappedCrowdsale: cap exceeded");
}
}
......@@ -48,7 +48,8 @@ contract IndividuallyCappedCrowdsale is Crowdsale, CapperRole {
*/
function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
super._preValidatePurchase(beneficiary, weiAmount);
require(_contributions[beneficiary].add(weiAmount) <= _caps[beneficiary]);
// solhint-disable-next-line max-line-length
require(_contributions[beneficiary].add(weiAmount) <= _caps[beneficiary], "IndividuallyCappedCrowdsale: beneficiary's cap exceeded");
}
/**
......
......@@ -24,7 +24,7 @@ contract TimedCrowdsale is Crowdsale {
* @dev Reverts if not in crowdsale time range.
*/
modifier onlyWhileOpen {
require(isOpen());
require(isOpen(), "TimedCrowdsale: not open");
_;
}
......@@ -35,8 +35,9 @@ contract TimedCrowdsale is Crowdsale {
*/
constructor (uint256 openingTime, uint256 closingTime) public {
// solhint-disable-next-line not-rely-on-time
require(openingTime >= block.timestamp);
require(closingTime > openingTime);
require(openingTime >= block.timestamp, "TimedCrowdsale: opening time is before current time");
// solhint-disable-next-line max-line-length
require(closingTime > openingTime, "TimedCrowdsale: opening time is not before closing time");
_openingTime = openingTime;
_closingTime = closingTime;
......@@ -87,8 +88,9 @@ contract TimedCrowdsale is Crowdsale {
* @param newClosingTime Crowdsale closing time
*/
function _extendTime(uint256 newClosingTime) internal {
require(!hasClosed());
require(newClosingTime > _closingTime);
require(!hasClosed(), "TimedCrowdsale: already closed");
// solhint-disable-next-line max-line-length
require(newClosingTime > _closingTime, "TimedCrowdsale: new closing time is before current closing time");
emit TimedCrowdsaleExtended(_closingTime, newClosingTime);
_closingTime = newClosingTime;
......
......@@ -15,7 +15,7 @@ contract WhitelistCrowdsale is WhitelistedRole, Crowdsale {
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal view {
require(isWhitelisted(_beneficiary));
require(isWhitelisted(_beneficiary), "WhitelistCrowdsale: beneficiary doesn't have the Whitelisted role");
super._preValidatePurchase(_beneficiary, _weiAmount);
}
}
......@@ -44,7 +44,7 @@ contract ERC20Migrator {
* @param legacyToken address of the old token contract
*/
constructor (IERC20 legacyToken) public {
require(address(legacyToken) != address(0));
require(address(legacyToken) != address(0), "ERC20Migrator: legacy token is the zero address");
_legacyToken = legacyToken;
}
......@@ -68,9 +68,10 @@ contract ERC20Migrator {
* @param newToken_ the token that will be minted
*/
function beginMigration(ERC20Mintable newToken_) public {
require(address(_newToken) == address(0));
require(address(newToken_) != address(0));
require(newToken_.isMinter(address(this)));
require(address(_newToken) == address(0), "ERC20Migrator: migration already started");
require(address(newToken_) != address(0), "ERC20Migrator: new token is the zero address");
//solhint-disable-next-line max-line-length
require(newToken_.isMinter(address(this)), "ERC20Migrator: not a minter for new token");
_newToken = newToken_;
}
......@@ -82,7 +83,7 @@ contract ERC20Migrator {
* @param amount amount of tokens to be migrated
*/
function migrate(address account, uint256 amount) public {
require(address(_newToken) != address(0));
require(address(_newToken) != address(0), "ERC20Migrator: migration not started");
_legacyToken.safeTransferFrom(account, address(this), amount);
_newToken.mint(account, amount);
}
......
......@@ -101,8 +101,9 @@ contract ERC20Snapshot is ERC20 {
function _valueAt(uint256 snapshotId, Snapshots storage snapshots)
private view returns (bool, uint256)
{
require(snapshotId > 0);
require(snapshotId <= _currentSnapshotId.current());
require(snapshotId > 0, "ERC20Snapshot: id is 0");
// solhint-disable-next-line max-line-length
require(snapshotId <= _currentSnapshotId.current(), "ERC20Snapshot: nonexistent id");
uint256 index = snapshots.ids.findUpperBound(snapshotId);
......
......@@ -51,7 +51,7 @@ contract SignatureBouncer is SignerRole {
* @dev Requires that a valid signature of a signer was provided.
*/
modifier onlyValidSignature(bytes memory signature) {
require(_isValidSignature(msg.sender, signature));
require(_isValidSignature(msg.sender, signature), "SignatureBouncer: invalid signature for caller");
_;
}
......@@ -59,7 +59,8 @@ contract SignatureBouncer is SignerRole {
* @dev Requires that a valid signature with a specified method of a signer was provided.
*/
modifier onlyValidSignatureAndMethod(bytes memory signature) {
require(_isValidSignatureAndMethod(msg.sender, signature));
// solhint-disable-next-line max-line-length
require(_isValidSignatureAndMethod(msg.sender, signature), "SignatureBouncer: invalid signature for caller and method");
_;
}
......@@ -67,7 +68,8 @@ contract SignatureBouncer is SignerRole {
* @dev Requires that a valid signature with a specified method and params of a signer was provided.
*/
modifier onlyValidSignatureAndData(bytes memory signature) {
require(_isValidSignatureAndData(msg.sender, signature));
// solhint-disable-next-line max-line-length
require(_isValidSignatureAndData(msg.sender, signature), "SignatureBouncer: invalid signature for caller and data");
_;
}
......@@ -97,7 +99,7 @@ contract SignatureBouncer is SignerRole {
* @return bool
*/
function _isValidSignatureAndData(address account, bytes memory signature) internal view returns (bool) {
require(msg.data.length > _SIGNATURE_SIZE);
require(msg.data.length > _SIGNATURE_SIZE, "SignatureBouncer: data is too short");
bytes memory data = new bytes(msg.data.length - _SIGNATURE_SIZE);
for (uint i = 0; i < data.length; i++) {
......
......@@ -18,10 +18,10 @@ library SignedSafeMath {
return 0;
}
require(!(a == -1 && b == INT256_MIN)); // This is the only case of overflow not detected by the check below
require(!(a == -1 && b == INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b);
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
......@@ -30,8 +30,8 @@ library SignedSafeMath {
* @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0); // Solidity only automatically asserts when dividing by 0
require(!(b == -1 && a == INT256_MIN)); // This is the only case of overflow
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
......@@ -43,7 +43,7 @@ library SignedSafeMath {
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a));
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
}
......@@ -53,7 +53,7 @@ library SignedSafeMath {
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a));
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
}
......
......@@ -47,10 +47,12 @@ contract TokenVesting is Ownable {
* @param revocable whether the vesting is revocable or not
*/
constructor (address beneficiary, uint256 start, uint256 cliffDuration, uint256 duration, bool revocable) public {
require(beneficiary != address(0));
require(cliffDuration <= duration);
require(duration > 0);
require(start.add(duration) > block.timestamp);
require(beneficiary != address(0), "TokenVesting: beneficiary is the zero address");
// solhint-disable-next-line max-line-length
require(cliffDuration <= duration, "TokenVesting: cliff is longer than duration");
require(duration > 0, "TokenVesting: duration is 0");
// solhint-disable-next-line max-line-length
require(start.add(duration) > block.timestamp, "TokenVesting: final time is before current time");
_beneficiary = beneficiary;
_revocable = revocable;
......@@ -115,7 +117,7 @@ contract TokenVesting is Ownable {
function release(IERC20 token) public {
uint256 unreleased = _releasableAmount(token);
require(unreleased > 0);
require(unreleased > 0, "TokenVesting: no tokens are due");
_released[address(token)] = _released[address(token)].add(unreleased);
......@@ -130,8 +132,8 @@ contract TokenVesting is Ownable {
* @param token ERC20 token which is being vested
*/
function revoke(IERC20 token) public onlyOwner {
require(_revocable);
require(!_revoked[address(token)]);
require(_revocable, "TokenVesting: cannot revoke");
require(!_revoked[address(token)], "TokenVesting: token already revoked");
uint256 balance = token.balanceOf(address(this));
......
......@@ -48,6 +48,6 @@ contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsal
{
//As goal needs to be met for a successful crowdsale
//the value needs to less or equal than a cap which is limit for accepted funds
require(goal <= cap);
require(goal <= cap, "SampleCrowdSale: goal is greater than cap");
}
}
......@@ -37,7 +37,7 @@ contract ERC165 is IERC165 {
* @dev Internal method for registering an interface.
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
......@@ -27,7 +27,7 @@ contract Pausable is PauserRole {
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!_paused);
require(!_paused, "Pausable: paused");
_;
}
......@@ -35,7 +35,7 @@ contract Pausable is PauserRole {
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(_paused);
require(_paused, "Pausable: not paused");
_;
}
......
......@@ -17,7 +17,7 @@ library SafeMath {
}
uint256 c = a * b;
require(c / a == b);
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
......@@ -27,7 +27,7 @@ library SafeMath {
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
......@@ -38,7 +38,7 @@ library SafeMath {
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
......@@ -49,7 +49,7 @@ library SafeMath {
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
require(c >= a, "SafeMath: addition overflow");
return c;
}
......@@ -59,7 +59,7 @@ library SafeMath {
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
......@@ -42,7 +42,7 @@ contract SupportsInterfaceWithLookupMock is IERC165 {
* @dev Private method for registering an interface.
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
require(interfaceId != 0xffffffff, "ERC165InterfacesSupported: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
......
......@@ -16,7 +16,7 @@ contract ERC721ReceiverMock is IERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4)
{
require(!_reverts);
require(!_reverts, "ERC721ReceiverMock: reverting");
emit Received(operator, from, tokenId, data, gasleft());
return _retval;
}
......
......@@ -4,6 +4,6 @@ contract ReentrancyAttack {
function callSender(bytes4 data) public {
// solhint-disable-next-line avoid-low-level-calls
(bool success,) = msg.sender.call(abi.encodeWithSelector(data));
require(success);
require(success, "ReentrancyAttack: failed call");
}
}
......@@ -26,7 +26,7 @@ contract ReentrancyMock is ReentrancyGuard {
count();
// solhint-disable-next-line avoid-low-level-calls
(bool success,) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1));
require(success);
require(success, "ReentrancyMock: failed call");
}
}
......
......@@ -26,7 +26,7 @@ contract ERC20ReturnFalseMock {
}
function allowance(address, address) public view returns (uint256) {
require(_dummy == 0);
require(_dummy == 0); // Duummy read from a state variable so that the function is view
return 0;
}
}
......
......@@ -30,7 +30,7 @@ contract Ownable {
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
require(isOwner(), "Ownable: caller is not the owner");
_;
}
......@@ -66,7 +66,7 @@ contract Ownable {
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
......
......@@ -23,7 +23,7 @@ contract Secondary {
* @dev Reverts if called from any account other than the primary.
*/
modifier onlyPrimary() {
require(msg.sender == _primary);
require(msg.sender == _primary, "Secondary: caller is not the primary account");
_;
}
......@@ -39,7 +39,7 @@ contract Secondary {
* @param recipient The address of new primary.
*/
function transferPrimary(address recipient) public onlyPrimary {
require(recipient != address(0));
require(recipient != address(0), "Secondary: new primary is the zero address");
_primary = recipient;
emit PrimaryTransferred(_primary);
}
......
......@@ -37,8 +37,9 @@ contract PaymentSplitter {
* duplicates in `payees`.
*/
constructor (address[] memory payees, uint256[] memory shares) public payable {
require(payees.length == shares.length);
require(payees.length > 0);
// solhint-disable-next-line max-line-length
require(payees.length == shares.length, "PaymentSplitter: payees and shares length mismatch");
require(payees.length > 0, "PaymentSplitter: no payees");
for (uint256 i = 0; i < payees.length; i++) {
_addPayee(payees[i], shares[i]);
......@@ -98,12 +99,12 @@ contract PaymentSplitter {
* total shares and their previous withdrawals.
*/
function release(address payable account) public {
require(_shares[account] > 0);
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 totalReceived = address(this).balance.add(_totalReleased);
uint256 payment = totalReceived.mul(_shares[account]).div(_totalShares).sub(_released[account]);
require(payment != 0);
require(payment != 0, "PaymentSplitter: account is not due payment");
_released[account] = _released[account].add(payment);
_totalReleased = _totalReleased.add(payment);
......@@ -118,9 +119,9 @@ contract PaymentSplitter {
* @param shares_ The number of shares owned by the payee.
*/
function _addPayee(address account, uint256 shares_) private {
require(account != address(0));
require(shares_ > 0);
require(_shares[account] == 0);
require(account != address(0), "PaymentSplitter: account is the zero address");
require(shares_ > 0, "PaymentSplitter: shares are 0");
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_payees.push(account);
_shares[account] = shares_;
......
......@@ -16,7 +16,7 @@ contract ConditionalEscrow is Escrow {
function withdrawalAllowed(address payee) public view returns (bool);
function withdraw(address payable payee) public {
require(withdrawalAllowed(payee));
require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw");
super.withdraw(payee);
}
}
......@@ -27,7 +27,7 @@ contract RefundEscrow is ConditionalEscrow {
* @param beneficiary The beneficiary of the deposits.
*/
constructor (address payable beneficiary) public {
require(beneficiary != address(0));
require(beneficiary != address(0), "RefundEscrow: beneficiary is the zero address");
_beneficiary = beneficiary;
_state = State.Active;
}
......@@ -51,7 +51,7 @@ contract RefundEscrow is ConditionalEscrow {
* @param refundee The address funds will be sent to if a refund occurs.
*/
function deposit(address refundee) public payable {
require(_state == State.Active);
require(_state == State.Active, "RefundEscrow: can only deposit while active");
super.deposit(refundee);
}
......@@ -60,7 +60,7 @@ contract RefundEscrow is ConditionalEscrow {
* further deposits.
*/
function close() public onlyPrimary {
require(_state == State.Active);
require(_state == State.Active, "RefundEscrow: can only close while active");
_state = State.Closed;
emit RefundsClosed();
}
......@@ -69,7 +69,7 @@ contract RefundEscrow is ConditionalEscrow {
* @dev Allows for refunds to take place, rejecting further deposits.
*/
function enableRefunds() public onlyPrimary {
require(_state == State.Active);
require(_state == State.Active, "RefundEscrow: can only enable refunds while active");
_state = State.Refunding;
emit RefundsEnabled();
}
......@@ -78,7 +78,7 @@ contract RefundEscrow is ConditionalEscrow {
* @dev Withdraws the beneficiary's funds.
*/
function beneficiaryWithdraw() public {
require(_state == State.Closed);
require(_state == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed");
_beneficiary.transfer(address(this).balance);
}
......
......@@ -125,7 +125,7 @@ contract ERC20 is IERC20 {
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(to != address(0));
require(to != address(0), "ERC20: transfer to the zero address");
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
......@@ -140,7 +140,7 @@ contract ERC20 is IERC20 {
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != address(0));
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
......@@ -154,7 +154,7 @@ contract ERC20 is IERC20 {
* @param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0));
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
......@@ -168,8 +168,8 @@ contract ERC20 is IERC20 {
* @param value The number of tokens that can be spent.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(spender != address(0));
require(owner != address(0));
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowed[owner][spender] = value;
emit Approval(owner, spender, value);
......
......@@ -10,7 +10,7 @@ contract ERC20Capped is ERC20Mintable {
uint256 private _cap;
constructor (uint256 cap) public {
require(cap > 0);
require(cap > 0, "ERC20Capped: cap is 0");
_cap = cap;
}
......@@ -22,7 +22,7 @@ contract ERC20Capped is ERC20Mintable {
}
function _mint(address account, uint256 value) internal {
require(totalSupply().add(value) <= _cap);
require(totalSupply().add(value) <= _cap, "ERC20Capped: cap exceeded");
super._mint(account, value);
}
}
......@@ -29,7 +29,10 @@ library SafeERC20 {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require((value == 0) || (token.allowance(address(this), spender) == 0));
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
......@@ -57,15 +60,16 @@ library SafeERC20 {
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
require(address(token).isContract());
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
require(abi.decode(returndata, (bool)));
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
......@@ -21,7 +21,7 @@ contract TokenTimelock {
constructor (IERC20 token, address beneficiary, uint256 releaseTime) public {
// solhint-disable-next-line not-rely-on-time
require(releaseTime > block.timestamp);
require(releaseTime > block.timestamp, "TokenTimelock: release time is before current time");
_token = token;
_beneficiary = beneficiary;
_releaseTime = releaseTime;
......@@ -53,10 +53,10 @@ contract TokenTimelock {
*/
function release() public {
// solhint-disable-next-line not-rely-on-time
require(block.timestamp >= _releaseTime);
require(block.timestamp >= _releaseTime, "TokenTimelock: current time is before release time");
uint256 amount = _token.balanceOf(address(this));
require(amount > 0);
require(amount > 0, "TokenTimelock: no tokens to release");
_token.safeTransfer(_beneficiary, amount);
}
......
......@@ -59,7 +59,8 @@ contract ERC721 is ERC165, IERC721 {
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
require(owner != address(0), "ERC721: balance query for the zero address");
return _ownedTokensCount[owner].current();
}
......@@ -70,7 +71,8 @@ contract ERC721 is ERC165, IERC721 {
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
......@@ -84,8 +86,11 @@ contract ERC721 is ERC165, IERC721 {
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
require(to != owner, "ERC721: transfer to current owner");
require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
"ERC721: approve caller is not owner nor approved for all"
);
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
......@@ -98,7 +103,8 @@ contract ERC721 is ERC165, IERC721 {
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
......@@ -109,7 +115,8 @@ contract ERC721 is ERC165, IERC721 {
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
require(to != msg.sender, "ERC721: approve to caller");
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
......@@ -133,7 +140,8 @@ contract ERC721 is ERC165, IERC721 {
* @param tokenId uint256 ID of the token to be transferred
*/
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
_transferFrom(from, to, tokenId);
}
......@@ -167,7 +175,7 @@ contract ERC721 is ERC165, IERC721 {
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data));
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
......@@ -188,6 +196,7 @@ contract ERC721 is ERC165, IERC721 {
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
......@@ -199,8 +208,8 @@ contract ERC721 is ERC165, IERC721 {
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId));
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_tokenOwner[tokenId] = to;
_ownedTokensCount[to].increment();
......@@ -216,7 +225,7 @@ contract ERC721 is ERC165, IERC721 {
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
require(ownerOf(tokenId) == owner);
require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
_clearApproval(tokenId);
......@@ -243,8 +252,8 @@ contract ERC721 is ERC165, IERC721 {
* @param tokenId uint256 ID of the token to be transferred
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from);
require(to != address(0));
require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_clearApproval(tokenId);
......
......@@ -12,7 +12,8 @@ contract ERC721Burnable is ERC721 {
* @param tokenId uint256 id of the ERC721 token to be burned.
*/
function burn(uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721Burnable: caller is not owner nor approved");
_burn(tokenId);
}
}
......@@ -45,7 +45,7 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
require(index < balanceOf(owner));
require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
......@@ -64,7 +64,7 @@ contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
* @return uint256 token ID at the given index of the tokens list
*/
function tokenByIndex(uint256 index) public view returns (uint256) {
require(index < totalSupply());
require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
......
......@@ -56,7 +56,7 @@ contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
* @param tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_exists(tokenId));
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
return _tokenURIs[tokenId];
}
......@@ -67,7 +67,7 @@ contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
* @param uri string URI to assign
*/
function _setTokenURI(uint256 tokenId, string memory uri) internal {
require(_exists(tokenId));
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = uri;
}
......
......@@ -27,6 +27,6 @@ contract ReentrancyGuard {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter);
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}
......@@ -9,7 +9,7 @@ contract('Roles', function ([_, authorized, otherAuthorized, other]) {
});
it('reverts when querying roles for the zero account', async function () {
await shouldFail.reverting(this.roles.has(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(this.roles.has(ZERO_ADDRESS), 'Roles: account is the zero address');
});
context('initially', function () {
......@@ -28,11 +28,11 @@ contract('Roles', function ([_, authorized, otherAuthorized, other]) {
it('reverts when adding roles to an already assigned account', async function () {
await this.roles.add(authorized);
await shouldFail.reverting(this.roles.add(authorized));
await shouldFail.reverting.withMessage(this.roles.add(authorized), 'Roles: account already has role');
});
it('reverts when adding roles to the zero account', async function () {
await shouldFail.reverting(this.roles.add(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(this.roles.add(ZERO_ADDRESS), 'Roles: account is the zero address');
});
});
});
......@@ -51,11 +51,11 @@ contract('Roles', function ([_, authorized, otherAuthorized, other]) {
});
it('reverts when removing unassigned roles', async function () {
await shouldFail.reverting(this.roles.remove(other));
await shouldFail.reverting.withMessage(this.roles.remove(other), 'Roles: account does not have role');
});
it('reverts when removing roles from the zero account', async function () {
await shouldFail.reverting(this.roles.remove(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(this.roles.remove(ZERO_ADDRESS), 'Roles: account is the zero address');
});
});
});
......
......@@ -39,7 +39,9 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolen
}
it('reverts when querying roles for the null account', async function () {
await shouldFail.reverting(this.contract[`is${rolename}`](ZERO_ADDRESS));
await shouldFail.reverting.withMessage(this.contract[`is${rolename}`](ZERO_ADDRESS),
'Roles: account is the zero address'
);
});
describe('access control', function () {
......@@ -55,7 +57,9 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolen
const from = other;
it('reverts', async function () {
await shouldFail.reverting(this.contract[`only${rolename}Mock`]({ from }));
await shouldFail.reverting.withMessage(this.contract[`only${rolename}Mock`]({ from }),
`${rolename}Role: caller does not have the ${rolename} role`
);
});
});
});
......@@ -75,11 +79,15 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolen
});
it('reverts when adding role to an already assigned account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](authorized, { from }));
await shouldFail.reverting.withMessage(this.contract[`add${rolename}`](authorized, { from }),
'Roles: account already has role'
);
});
it('reverts when adding role to the null account', async function () {
await shouldFail.reverting(this.contract[`add${rolename}`](ZERO_ADDRESS, { from }));
await shouldFail.reverting.withMessage(this.contract[`add${rolename}`](ZERO_ADDRESS, { from }),
'Roles: account is the zero address'
);
});
});
});
......@@ -101,11 +109,15 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolen
});
it('reverts when removing from an unassigned account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](other, { from }));
await shouldFail.reverting.withMessage(this.contract[`remove${rolename}`](other, { from }),
'Roles: account does not have role'
);
});
it('reverts when removing role from the null account', async function () {
await shouldFail.reverting(this.contract[`remove${rolename}`](ZERO_ADDRESS, { from }));
await shouldFail.reverting.withMessage(this.contract[`remove${rolename}`](ZERO_ADDRESS, { from }),
'Roles: account is the zero address'
);
});
});
});
......@@ -122,7 +134,9 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [other], rolen
});
it('reverts when renouncing unassigned role', async function () {
await shouldFail.reverting(this.contract[`renounce${rolename}`]({ from: other }));
await shouldFail.reverting.withMessage(this.contract[`renounce${rolename}`]({ from: other }),
'Roles: account does not have role'
);
});
});
});
......
......@@ -75,7 +75,9 @@ contract('AllowanceCrowdsale', function ([_, investor, wallet, purchaser, tokenW
describe('when token wallet is the zero address', function () {
it('creation reverts', async function () {
this.token = await SimpleToken.new({ from: tokenWallet });
await shouldFail.reverting(AllowanceCrowdsaleImpl.new(rate, wallet, this.token.address, ZERO_ADDRESS));
await shouldFail.reverting.withMessage(AllowanceCrowdsaleImpl.new(rate, wallet, this.token.address, ZERO_ADDRESS),
'AllowanceCrowdsale: token wallet is the zero address'
);
});
});
});
......@@ -14,7 +14,9 @@ contract('CappedCrowdsale', function ([_, wallet]) {
});
it('rejects a cap of zero', async function () {
await shouldFail.reverting(CappedCrowdsaleImpl.new(rate, wallet, this.token.address, 0));
await shouldFail.reverting.withMessage(CappedCrowdsaleImpl.new(rate, wallet, this.token.address, 0),
'CappedCrowdsale: cap is 0'
);
});
context('with crowdsale', function () {
......@@ -31,11 +33,11 @@ contract('CappedCrowdsale', function ([_, wallet]) {
it('should reject payments outside cap', async function () {
await this.crowdsale.send(cap);
await shouldFail.reverting(this.crowdsale.send(1));
await shouldFail.reverting.withMessage(this.crowdsale.send(1), 'CappedCrowdsale: cap exceeded');
});
it('should reject payments that exceed cap', async function () {
await shouldFail.reverting(this.crowdsale.send(cap.addn(1)));
await shouldFail.reverting.withMessage(this.crowdsale.send(cap.addn(1)), 'CappedCrowdsale: cap exceeded');
});
});
......
......@@ -11,8 +11,9 @@ contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
const expectedTokenAmount = rate.mul(value);
it('requires a non-null token', async function () {
await shouldFail.reverting(
Crowdsale.new(rate, wallet, ZERO_ADDRESS)
await shouldFail.reverting.withMessage(
Crowdsale.new(rate, wallet, ZERO_ADDRESS),
'Crowdsale: token is the zero address'
);
});
......@@ -22,14 +23,14 @@ contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
});
it('requires a non-zero rate', async function () {
await shouldFail.reverting(
Crowdsale.new(0, wallet, this.token.address)
await shouldFail.reverting.withMessage(
Crowdsale.new(0, wallet, this.token.address), 'Crowdsale: rate is 0'
);
});
it('requires a non-null wallet', async function () {
await shouldFail.reverting(
Crowdsale.new(rate, ZERO_ADDRESS, this.token.address)
await shouldFail.reverting.withMessage(
Crowdsale.new(rate, ZERO_ADDRESS, this.token.address), 'Crowdsale: wallet is the zero address'
);
});
......@@ -46,8 +47,8 @@ contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
});
it('reverts on zero-valued payments', async function () {
await shouldFail.reverting(
this.crowdsale.send(0, { from: purchaser })
await shouldFail.reverting.withMessage(
this.crowdsale.send(0, { from: purchaser }), 'Crowdsale: weiAmount is 0'
);
});
});
......@@ -58,14 +59,15 @@ contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
});
it('reverts on zero-valued payments', async function () {
await shouldFail.reverting(
this.crowdsale.buyTokens(investor, { value: 0, from: purchaser })
await shouldFail.reverting.withMessage(
this.crowdsale.buyTokens(investor, { value: 0, from: purchaser }), 'Crowdsale: weiAmount is 0'
);
});
it('requires a non-null beneficiary', async function () {
await shouldFail.reverting(
this.crowdsale.buyTokens(ZERO_ADDRESS, { value: value, from: purchaser })
await shouldFail.reverting.withMessage(
this.crowdsale.buyTokens(ZERO_ADDRESS, { value: value, from: purchaser }),
'Crowdsale: beneficiary is the zero address'
);
});
});
......
......@@ -23,7 +23,9 @@ contract('FinalizableCrowdsale', function ([_, wallet, other]) {
});
it('cannot be finalized before ending', async function () {
await shouldFail.reverting(this.crowdsale.finalize({ from: other }));
await shouldFail.reverting.withMessage(this.crowdsale.finalize({ from: other }),
'FinalizableCrowdsale: not closed'
);
});
it('can be finalized by anyone after ending', async function () {
......@@ -34,7 +36,9 @@ contract('FinalizableCrowdsale', function ([_, wallet, other]) {
it('cannot be finalized twice', async function () {
await time.increaseTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: other });
await shouldFail.reverting(this.crowdsale.finalize({ from: other }));
await shouldFail.reverting.withMessage(this.crowdsale.finalize({ from: other }),
'FinalizableCrowdsale: already finalized'
);
});
it('logs finalized', async function () {
......
......@@ -26,21 +26,21 @@ contract('IncreasingPriceCrowdsale', function ([_, investor, wallet, purchaser])
});
it('reverts with a final rate larger than the initial rate', async function () {
await shouldFail.reverting(IncreasingPriceCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(IncreasingPriceCrowdsaleImpl.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, initialRate.addn(1)
));
), 'IncreasingPriceCrowdsale: initial rate is not greater than final rate');
});
it('reverts with a final rate equal to the initial rate', async function () {
await shouldFail.reverting(IncreasingPriceCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(IncreasingPriceCrowdsaleImpl.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, initialRate
));
), 'IncreasingPriceCrowdsale: initial rate is not greater than final rate');
});
it('reverts with a final rate of zero', async function () {
await shouldFail.reverting(IncreasingPriceCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(IncreasingPriceCrowdsaleImpl.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, 0
));
), 'IncreasingPriceCrowdsale: final rate is 0');
});
context('with crowdsale', function () {
......@@ -57,7 +57,9 @@ contract('IncreasingPriceCrowdsale', function ([_, investor, wallet, purchaser])
});
it('reverts when the base Crowdsale\'s rate function is called', async function () {
await shouldFail.reverting(this.crowdsale.rate());
await shouldFail.reverting.withMessage(this.crowdsale.rate(),
'IncreasingPriceCrowdsale: rate() called'
);
});
it('returns a rate of 0 before the crowdsale starts', async function () {
......
......@@ -34,7 +34,9 @@ contract('IndividuallyCappedCrowdsale', function (
});
it('reverts when a non-capper sets a cap', async function () {
await shouldFail.reverting(this.crowdsale.setCap(alice, capAlice, { from: other }));
await shouldFail.reverting.withMessage(this.crowdsale.setCap(alice, capAlice, { from: other }),
'CapperRole: caller does not have the Capper role'
);
});
context('with individual caps', function () {
......@@ -52,21 +54,31 @@ contract('IndividuallyCappedCrowdsale', function (
it('should reject payments outside cap', async function () {
await this.crowdsale.buyTokens(alice, { value: capAlice });
await shouldFail.reverting(this.crowdsale.buyTokens(alice, { value: 1 }));
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(alice, { value: 1 }),
'IndividuallyCappedCrowdsale: beneficiary\'s cap exceeded'
);
});
it('should reject payments that exceed cap', async function () {
await shouldFail.reverting(this.crowdsale.buyTokens(alice, { value: capAlice.addn(1) }));
await shouldFail.reverting(this.crowdsale.buyTokens(bob, { value: capBob.addn(1) }));
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(alice, { value: capAlice.addn(1) }),
'IndividuallyCappedCrowdsale: beneficiary\'s cap exceeded'
);
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(bob, { value: capBob.addn(1) }),
'IndividuallyCappedCrowdsale: beneficiary\'s cap exceeded'
);
});
it('should manage independent caps', async function () {
await this.crowdsale.buyTokens(alice, { value: lessThanCapAlice });
await shouldFail.reverting(this.crowdsale.buyTokens(bob, { value: lessThanCapAlice }));
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(bob, { value: lessThanCapAlice }),
'IndividuallyCappedCrowdsale: beneficiary\'s cap exceeded'
);
});
it('should default to a cap of zero', async function () {
await shouldFail.reverting(this.crowdsale.buyTokens(charlie, { value: lessThanCapBoth }));
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(charlie, { value: lessThanCapBoth }),
'IndividuallyCappedCrowdsale: beneficiary\'s cap exceeded'
);
});
});
......
......@@ -26,8 +26,12 @@ contract('PausableCrowdsale', function ([_, pauser, wallet, other]) {
});
it('purchases do not work', async function () {
await shouldFail.reverting(this.crowdsale.sendTransaction({ from: other, value }));
await shouldFail.reverting(this.crowdsale.buyTokens(other, { from: other, value }));
await shouldFail.reverting.withMessage(this.crowdsale.sendTransaction({ from: other, value }),
'Pausable: paused'
);
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(other, { from: other, value }),
'Pausable: paused'
);
});
context('after unpause', function () {
......
......@@ -41,7 +41,9 @@ contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
});
it('does not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'PostDeliveryCrowdsale: not closed'
);
});
context('after closing time', function () {
......@@ -57,7 +59,9 @@ contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
it('rejects multiple withdrawals', async function () {
await this.crowdsale.withdrawTokens(investor);
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'PostDeliveryCrowdsale: beneficiary is not due any tokens'
);
});
});
});
......
......@@ -24,8 +24,9 @@ contract('RefundableCrowdsale', function ([_, wallet, investor, purchaser, other
});
it('rejects a goal of zero', async function () {
await shouldFail.reverting(
RefundableCrowdsaleImpl.new(this.openingTime, this.closingTime, rate, wallet, this.token.address, 0)
await shouldFail.reverting.withMessage(
RefundableCrowdsaleImpl.new(this.openingTime, this.closingTime, rate, wallet, this.token.address, 0),
'RefundableCrowdsale: goal is 0'
);
});
......@@ -40,7 +41,9 @@ contract('RefundableCrowdsale', function ([_, wallet, investor, purchaser, other
context('before opening time', function () {
it('denies refunds', async function () {
await shouldFail.reverting(this.crowdsale.claimRefund(investor));
await shouldFail.reverting.withMessage(this.crowdsale.claimRefund(investor),
'RefundableCrowdsale: not finalized'
);
});
});
......@@ -50,7 +53,9 @@ contract('RefundableCrowdsale', function ([_, wallet, investor, purchaser, other
});
it('denies refunds', async function () {
await shouldFail.reverting(this.crowdsale.claimRefund(investor));
await shouldFail.reverting.withMessage(this.crowdsale.claimRefund(investor),
'RefundableCrowdsale: not finalized'
);
});
context('with unreached goal', function () {
......@@ -84,7 +89,9 @@ contract('RefundableCrowdsale', function ([_, wallet, investor, purchaser, other
});
it('denies refunds', async function () {
await shouldFail.reverting(this.crowdsale.claimRefund(investor));
await shouldFail.reverting.withMessage(this.crowdsale.claimRefund(investor),
'RefundableCrowdsale: goal reached'
);
});
it('forwards funds to wallet', async function () {
......
......@@ -42,7 +42,9 @@ contract('RefundablePostDeliveryCrowdsale', function ([_, investor, wallet, purc
});
it('does not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'RefundablePostDeliveryCrowdsale: not finalized'
);
});
context('after closing time and finalization', function () {
......@@ -52,7 +54,9 @@ contract('RefundablePostDeliveryCrowdsale', function ([_, investor, wallet, purc
});
it('rejects token withdrawals', async function () {
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'RefundablePostDeliveryCrowdsale: goal not reached'
);
});
});
});
......@@ -70,7 +74,9 @@ contract('RefundablePostDeliveryCrowdsale', function ([_, investor, wallet, purc
});
it('does not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'RefundablePostDeliveryCrowdsale: not finalized'
);
});
context('after closing time and finalization', function () {
......@@ -87,7 +93,9 @@ contract('RefundablePostDeliveryCrowdsale', function ([_, investor, wallet, purc
it('rejects multiple withdrawals', async function () {
await this.crowdsale.withdrawTokens(investor);
await shouldFail.reverting(this.crowdsale.withdrawTokens(investor));
await shouldFail.reverting.withMessage(this.crowdsale.withdrawTokens(investor),
'PostDeliveryCrowdsale: beneficiary is not due any tokens'
);
});
});
});
......
......@@ -21,21 +21,21 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
});
it('reverts if the opening time is in the past', async function () {
await shouldFail.reverting(TimedCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(TimedCrowdsaleImpl.new(
(await time.latest()).sub(time.duration.days(1)), this.closingTime, rate, wallet, this.token.address
));
), 'TimedCrowdsale: opening time is before current time');
});
it('reverts if the closing time is before the opening time', async function () {
await shouldFail.reverting(TimedCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(TimedCrowdsaleImpl.new(
this.openingTime, this.openingTime.sub(time.duration.seconds(1)), rate, wallet, this.token.address
));
), 'TimedCrowdsale: opening time is not before closing time');
});
it('reverts if the closing time equals the opening time', async function () {
await shouldFail.reverting(TimedCrowdsaleImpl.new(
await shouldFail.reverting.withMessage(TimedCrowdsaleImpl.new(
this.openingTime, this.openingTime, rate, wallet, this.token.address
));
), 'TimedCrowdsale: opening time is not before closing time');
});
context('with crowdsale', function () {
......@@ -56,8 +56,10 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
describe('accepting payments', function () {
it('should reject payments before start', async function () {
(await this.crowdsale.isOpen()).should.equal(false);
await shouldFail.reverting(this.crowdsale.send(value));
await shouldFail.reverting(this.crowdsale.buyTokens(investor, { from: purchaser, value: value }));
await shouldFail.reverting.withMessage(this.crowdsale.send(value), 'TimedCrowdsale: not open');
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(investor, { from: purchaser, value: value }),
'TimedCrowdsale: not open'
);
});
it('should accept payments after start', async function () {
......@@ -69,25 +71,31 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
it('should reject payments after end', async function () {
await time.increaseTo(this.afterClosingTime);
await shouldFail.reverting(this.crowdsale.send(value));
await shouldFail.reverting(this.crowdsale.buyTokens(investor, { value: value, from: purchaser }));
await shouldFail.reverting.withMessage(this.crowdsale.send(value), 'TimedCrowdsale: not open');
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(investor, { value: value, from: purchaser }),
'TimedCrowdsale: not open'
);
});
});
describe('extending closing time', function () {
it('should not reduce duration', async function () {
// Same date
await shouldFail.reverting(this.crowdsale.extendTime(this.closingTime));
await shouldFail.reverting.withMessage(this.crowdsale.extendTime(this.closingTime),
'TimedCrowdsale: new closing time is before current closing time'
);
// Prescending date
const newClosingTime = this.closingTime.sub(time.duration.seconds(1));
await shouldFail.reverting(this.crowdsale.extendTime(newClosingTime));
await shouldFail.reverting.withMessage(this.crowdsale.extendTime(newClosingTime),
'TimedCrowdsale: new closing time is before current closing time'
);
});
context('before crowdsale start', function () {
beforeEach(async function () {
(await this.crowdsale.isOpen()).should.equal(false);
await shouldFail.reverting(this.crowdsale.send(value));
await shouldFail.reverting.withMessage(this.crowdsale.send(value), 'TimedCrowdsale: not open');
});
it('it extends end time', async function () {
......@@ -126,7 +134,9 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
it('it reverts', async function () {
const newClosingTime = await time.latest();
await shouldFail.reverting(this.crowdsale.extendTime(newClosingTime));
await shouldFail.reverting.withMessage(this.crowdsale.extendTime(newClosingTime),
'TimedCrowdsale: already closed'
);
});
});
});
......
......@@ -20,8 +20,12 @@ contract('WhitelistCrowdsale', function ([_, wallet, whitelister, whitelisted, o
}
async function purchaseShouldFail (crowdsale, beneficiary, value) {
await shouldFail.reverting(crowdsale.buyTokens(beneficiary, { from: beneficiary, value }));
await shouldFail.reverting(crowdsale.sendTransaction({ from: beneficiary, value }));
await shouldFail.reverting.withMessage(crowdsale.buyTokens(beneficiary, { from: beneficiary, value }),
'WhitelistCrowdsale: beneficiary doesn\'t have the Whitelisted role'
);
await shouldFail.reverting.withMessage(crowdsale.sendTransaction({ from: beneficiary, value }),
'WhitelistCrowdsale: beneficiary doesn\'t have the Whitelisted role'
);
}
context('with no whitelisted addresses', function () {
......
......@@ -118,8 +118,9 @@ contract('ECDSA', function ([_, other]) {
it.skip('reverts', async function () {
// Create the signature
const signature = await web3.eth.sign(TEST_MESSAGE, other);
await shouldFail.reverting(
this.ecdsa.recover(TEST_MESSAGE.substring(2), signature)
await shouldFail.reverting.withMessage(
this.ecdsa.recover(TEST_MESSAGE.substring(2), signature),
'Failure message'
);
});
});
......
......@@ -39,7 +39,7 @@ contract('Counters', function () {
it('reverts if the current value is 0', async function () {
await this.counter.decrement();
await shouldFail.reverting(this.counter.decrement());
await shouldFail.reverting.withMessage(this.counter.decrement(), 'SafeMath: subtraction overflow');
});
it('can be called multiple times', async function () {
......
......@@ -21,10 +21,11 @@ contract('ERC1820Implementer', function ([_, registryFunder, implementee, other]
});
it('reverts when attempting to set as implementer in the registry', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.registry.setInterfaceImplementer(
implementee, this.interfaceA, this.implementer.address, { from: implementee }
)
),
'Does not implement the interface'
);
});
});
......
......@@ -9,7 +9,9 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
const totalSupply = new BN('200');
it('reverts with a null legacy token address', async function () {
await shouldFail.reverting(ERC20Migrator.new(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(ERC20Migrator.new(ZERO_ADDRESS),
'ERC20Migrator: legacy token is the zero address'
);
});
describe('with tokens and migrator', function () {
......@@ -25,11 +27,15 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
describe('beginMigration', function () {
it('reverts with a null new token address', async function () {
await shouldFail.reverting(this.migrator.beginMigration(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(this.migrator.beginMigration(ZERO_ADDRESS),
'ERC20Migrator: new token is the zero address'
);
});
it('reverts if not a minter of the token', async function () {
await shouldFail.reverting(this.migrator.beginMigration(this.newToken.address));
await shouldFail.reverting.withMessage(this.migrator.beginMigration(this.newToken.address),
'ERC20Migrator: not a minter for new token'
);
});
it('succeeds if it is a minter of the token', async function () {
......@@ -40,7 +46,9 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
it('reverts the second time it is called', async function () {
await this.newToken.addMinter(this.migrator.address);
await this.migrator.beginMigration(this.newToken.address);
await shouldFail.reverting(this.migrator.beginMigration(this.newToken.address));
await shouldFail.reverting.withMessage(this.migrator.beginMigration(this.newToken.address),
'ERC20Migrator: migration already started'
);
});
});
......@@ -58,7 +66,9 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
});
it('reverts', async function () {
await shouldFail.reverting(this.migrator.migrateAll(owner));
await shouldFail.reverting.withMessage(this.migrator.migrateAll(owner),
'ERC20Migrator: migration not started'
);
});
});
});
......@@ -72,7 +82,9 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
});
it('reverts', async function () {
await shouldFail.reverting(this.migrator.migrate(owner, amount));
await shouldFail.reverting.withMessage(this.migrator.migrate(owner, amount),
'ERC20Migrator: migration not started'
);
});
});
});
......@@ -174,7 +186,9 @@ contract('ERC20Migrator', function ([_, owner, recipient, anotherAccount]) {
const amount = baseAmount.addn(1);
it('reverts', async function () {
await shouldFail.reverting(this.migrator.migrate(owner, amount));
await shouldFail.reverting.withMessage(this.migrator.migrate(owner, amount),
'SafeERC20: low-level call failed'
);
});
});
});
......
......@@ -24,11 +24,11 @@ contract('ERC20Snapshot', function ([_, initialHolder, recipient, other]) {
describe('totalSupplyAt', function () {
it('reverts with a snapshot id of 0', async function () {
await shouldFail.reverting(this.token.totalSupplyAt(0));
await shouldFail.reverting.withMessage(this.token.totalSupplyAt(0), 'ERC20Snapshot: id is 0');
});
it('reverts with a not-yet-created snapshot id', async function () {
await shouldFail.reverting(this.token.totalSupplyAt(1));
await shouldFail.reverting.withMessage(this.token.totalSupplyAt(1), 'ERC20Snapshot: nonexistent id');
});
context('with initial snapshot', function () {
......@@ -98,11 +98,11 @@ contract('ERC20Snapshot', function ([_, initialHolder, recipient, other]) {
describe('balanceOfAt', function () {
it('reverts with a snapshot id of 0', async function () {
await shouldFail.reverting(this.token.balanceOfAt(other, 0));
await shouldFail.reverting.withMessage(this.token.balanceOfAt(other, 0), 'ERC20Snapshot: id is 0');
});
it('reverts with a not-yet-created snapshot id', async function () {
await shouldFail.reverting(this.token.balanceOfAt(other, 1));
await shouldFail.reverting.withMessage(this.token.balanceOfAt(other, 1), 'ERC20Snapshot: nonexistent id');
});
context('with initial snapshot', function () {
......
......@@ -30,21 +30,23 @@ contract('SignatureBouncer', function ([_, signer, otherSigner, other, authorize
});
it('does not allow invalid signature for sender', async function () {
await shouldFail.reverting(
this.sigBouncer.onlyWithValidSignature(INVALID_SIGNATURE, { from: authorizedUser })
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignature(INVALID_SIGNATURE, { from: authorizedUser }),
'SignatureBouncer: invalid signature for caller'
);
});
it('does not allow valid signature for other sender', async function () {
await shouldFail.reverting(
this.sigBouncer.onlyWithValidSignature(await this.signFor(authorizedUser), { from: other })
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignature(await this.signFor(authorizedUser), { from: other }),
'SignatureBouncer: invalid signature for caller'
);
});
it('does not allow valid signature for method for sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignature(await this.signFor(authorizedUser, 'onlyWithValidSignature'),
{ from: authorizedUser })
{ from: authorizedUser }), 'SignatureBouncer: invalid signature for caller'
);
});
});
......@@ -57,29 +59,32 @@ contract('SignatureBouncer', function ([_, signer, otherSigner, other, authorize
});
it('does not allow invalid signature with correct method for sender', async function () {
await shouldFail.reverting(
this.sigBouncer.onlyWithValidSignatureAndMethod(INVALID_SIGNATURE, { from: authorizedUser })
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndMethod(INVALID_SIGNATURE, { from: authorizedUser }),
'SignatureBouncer: invalid signature for caller and method'
);
});
it('does not allow valid signature with correct method for other sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndMethod(
await this.signFor(authorizedUser, 'onlyWithValidSignatureAndMethod'), { from: other }
)
),
'SignatureBouncer: invalid signature for caller and method'
);
});
it('does not allow valid method signature with incorrect method for sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndMethod(await this.signFor(authorizedUser, 'theWrongMethod'),
{ from: authorizedUser })
{ from: authorizedUser }), 'SignatureBouncer: invalid signature for caller and method'
);
});
it('does not allow valid non-method signature method for sender', async function () {
await shouldFail.reverting(
this.sigBouncer.onlyWithValidSignatureAndMethod(await this.signFor(authorizedUser), { from: authorizedUser })
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndMethod(await this.signFor(authorizedUser), { from: authorizedUser }),
'SignatureBouncer: invalid signature for caller and method'
);
});
});
......@@ -92,40 +97,41 @@ contract('SignatureBouncer', function ([_, signer, otherSigner, other, authorize
});
it('does not allow invalid signature with correct method and data for sender', async function () {
await shouldFail.reverting(
this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, INVALID_SIGNATURE, { from: authorizedUser })
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, INVALID_SIGNATURE, { from: authorizedUser }),
'SignatureBouncer: invalid signature for caller and data'
);
});
it('does not allow valid signature with correct method and incorrect data for sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE + 10,
await this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]),
{ from: authorizedUser }
)
), 'SignatureBouncer: invalid signature for caller and data'
);
});
it('does not allow valid signature with correct method and data for other sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE,
await this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]),
{ from: other }
)
), 'SignatureBouncer: invalid signature for caller and data'
);
});
it('does not allow valid non-method signature for sender', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE,
await this.signFor(authorizedUser), { from: authorizedUser }
)
), 'SignatureBouncer: invalid signature for caller and data'
);
});
it('does not allow msg.data shorter than SIGNATURE_SIZE', async function () {
await shouldFail.reverting(
this.sigBouncer.tooShortMsgData({ from: authorizedUser })
await shouldFail.reverting.withMessage(
this.sigBouncer.tooShortMsgData({ from: authorizedUser }), 'SignatureBouncer: data is too short'
);
});
});
......
......@@ -13,9 +13,9 @@ contract('SignedSafeMath', function () {
(await fn(rhs, lhs)).should.be.bignumber.equal(expected);
}
async function testFailsCommutative (fn, lhs, rhs) {
await shouldFail.reverting(fn(lhs, rhs));
await shouldFail.reverting(fn(rhs, lhs));
async function testFailsCommutative (fn, lhs, rhs, reason) {
await shouldFail.reverting.withMessage(fn(lhs, rhs), reason);
await shouldFail.reverting.withMessage(fn(rhs, lhs), reason);
}
describe('add', function () {
......@@ -37,14 +37,14 @@ contract('SignedSafeMath', function () {
const a = MAX_INT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.add, a, b);
await testFailsCommutative(this.safeMath.add, a, b, 'SignedSafeMath: addition overflow');
});
it('reverts on negative addition overflow', async function () {
const a = MIN_INT256;
const b = new BN('-1');
await testFailsCommutative(this.safeMath.add, a, b);
await testFailsCommutative(this.safeMath.add, a, b, 'SignedSafeMath: addition overflow');
});
});
......@@ -69,14 +69,14 @@ contract('SignedSafeMath', function () {
const a = MAX_INT256;
const b = new BN('-1');
await shouldFail.reverting(this.safeMath.sub(a, b));
await shouldFail.reverting.withMessage(this.safeMath.sub(a, b), 'SignedSafeMath: subtraction overflow');
});
it('reverts on negative subtraction overflow', async function () {
const a = MIN_INT256;
const b = new BN('1');
await shouldFail.reverting(this.safeMath.sub(a, b));
await shouldFail.reverting.withMessage(this.safeMath.sub(a, b), 'SignedSafeMath: subtraction overflow');
});
});
......@@ -99,14 +99,14 @@ contract('SignedSafeMath', function () {
const a = MAX_INT256;
const b = new BN('2');
await testFailsCommutative(this.safeMath.mul, a, b);
await testFailsCommutative(this.safeMath.mul, a, b, 'SignedSafeMath: multiplication overflow');
});
it('reverts when minimum integer is multiplied by -1', async function () {
const a = MIN_INT256;
const b = new BN('-1');
await testFailsCommutative(this.safeMath.mul, a, b);
await testFailsCommutative(this.safeMath.mul, a, b, 'SignedSafeMath: multiplication overflow');
});
});
......@@ -137,14 +137,14 @@ contract('SignedSafeMath', function () {
const a = new BN('-5678');
const b = new BN('0');
await shouldFail.reverting(this.safeMath.div(a, b));
await shouldFail.reverting.withMessage(this.safeMath.div(a, b), 'SignedSafeMath: division by zero');
});
it('reverts on overflow, negative second', async function () {
const a = new BN(MIN_INT256);
const b = new BN('-1');
await shouldFail.reverting(this.safeMath.div(a, b));
await shouldFail.reverting.withMessage(this.safeMath.div(a, b), 'SignedSafeMath: division overflow');
});
});
});
......@@ -20,21 +20,23 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
cliffDuration.should.be.bignumber.that.is.at.least(duration);
await shouldFail.reverting(
TokenVesting.new(beneficiary, this.start, cliffDuration, duration, true, { from: owner })
await shouldFail.reverting.withMessage(
TokenVesting.new(beneficiary, this.start, cliffDuration, duration, true, { from: owner }),
'TokenVesting: cliff is longer than duration'
);
});
it('reverts with a null beneficiary', async function () {
await shouldFail.reverting(
TokenVesting.new(ZERO_ADDRESS, this.start, this.cliffDuration, this.duration, true, { from: owner })
await shouldFail.reverting.withMessage(
TokenVesting.new(ZERO_ADDRESS, this.start, this.cliffDuration, this.duration, true, { from: owner }),
'TokenVesting: beneficiary is the zero address'
);
});
it('reverts with a null duration', async function () {
// cliffDuration should also be 0, since the duration must be larger than the cliff
await shouldFail.reverting(
TokenVesting.new(beneficiary, this.start, 0, 0, true, { from: owner })
await shouldFail.reverting.withMessage(
TokenVesting.new(beneficiary, this.start, 0, 0, true, { from: owner }), 'TokenVesting: duration is 0'
);
});
......@@ -42,8 +44,9 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
const now = await time.latest();
this.start = now.sub(this.duration).sub(time.duration.minutes(1));
await shouldFail.reverting(
TokenVesting.new(beneficiary, this.start, this.cliffDuration, this.duration, true, { from: owner })
await shouldFail.reverting.withMessage(
TokenVesting.new(beneficiary, this.start, this.cliffDuration, this.duration, true, { from: owner }),
'TokenVesting: final time is before current time'
);
});
......@@ -65,7 +68,9 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
});
it('cannot be released before cliff', async function () {
await shouldFail.reverting(this.vesting.release(this.token.address));
await shouldFail.reverting.withMessage(this.vesting.release(this.token.address),
'TokenVesting: no tokens are due'
);
});
it('can be released after cliff', async function () {
......@@ -121,7 +126,9 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
beneficiary, this.start, this.cliffDuration, this.duration, false, { from: owner }
);
await shouldFail.reverting(vesting.revoke(this.token.address, { from: owner }));
await shouldFail.reverting.withMessage(vesting.revoke(this.token.address, { from: owner }),
'TokenVesting: cannot revoke'
);
});
it('should return the non-vested tokens when revoked by owner', async function () {
......@@ -148,7 +155,9 @@ contract('TokenVesting', function ([_, owner, beneficiary]) {
it('should fail to be revoked a second time', async function () {
await this.vesting.revoke(this.token.address, { from: owner });
await shouldFail.reverting(this.vesting.revoke(this.token.address, { from: owner }));
await shouldFail.reverting.withMessage(this.vesting.revoke(this.token.address, { from: owner }),
'TokenVesting: token already revoked'
);
});
function vestedAmount (total, now, start, cliffDuration, duration) {
......
......@@ -38,8 +38,10 @@ contract('SampleCrowdsale', function ([_, deployer, owner, wallet, investor]) {
});
it('should not accept payments before start', async function () {
await shouldFail.reverting(this.crowdsale.send(ether('1')));
await shouldFail.reverting(this.crowdsale.buyTokens(investor, { from: investor, value: ether('1') }));
await shouldFail.reverting.withMessage(this.crowdsale.send(ether('1')), 'TimedCrowdsale: not open');
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(investor, { from: investor, value: ether('1') }),
'TimedCrowdsale: not open'
);
});
it('should accept payments during the sale', async function () {
......@@ -55,14 +57,16 @@ contract('SampleCrowdsale', function ([_, deployer, owner, wallet, investor]) {
it('should reject payments after end', async function () {
await time.increaseTo(this.afterClosingTime);
await shouldFail.reverting(this.crowdsale.send(ether('1')));
await shouldFail.reverting(this.crowdsale.buyTokens(investor, { value: ether('1'), from: investor }));
await shouldFail.reverting.withMessage(this.crowdsale.send(ether('1')), 'TimedCrowdsale: not open');
await shouldFail.reverting.withMessage(this.crowdsale.buyTokens(investor, { value: ether('1'), from: investor }),
'TimedCrowdsale: not open'
);
});
it('should reject payments over cap', async function () {
await time.increaseTo(this.openingTime);
await this.crowdsale.send(CAP);
await shouldFail.reverting(this.crowdsale.send(1));
await shouldFail.reverting.withMessage(this.crowdsale.send(1), 'CappedCrowdsale: cap exceeded');
});
it('should allow finalization and transfer funds to wallet if the goal is reached', async function () {
......@@ -93,9 +97,9 @@ contract('SampleCrowdsale', function ([_, deployer, owner, wallet, investor]) {
const HIGH_GOAL = ether('30');
it('creation reverts', async function () {
await shouldFail.reverting(SampleCrowdsale.new(
await shouldFail.reverting.withMessage(SampleCrowdsale.new(
this.openingTime, this.closingTime, RATE, wallet, CAP, this.token.address, HIGH_GOAL
));
), 'SampleCrowdSale: goal is greater than cap');
});
});
});
......@@ -9,7 +9,7 @@ contract('ERC165', function () {
});
it('does not allow 0xffffffff', async function () {
await shouldFail.reverting(this.mock.registerInterface('0xffffffff'));
await shouldFail.reverting.withMessage(this.mock.registerInterface('0xffffffff'), 'ERC165: invalid interface id');
});
shouldSupportInterfaces([
......
......@@ -30,7 +30,9 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('cannot take drastic measure in non-pause', async function () {
await shouldFail.reverting(this.pausable.drasticMeasure({ from: other }));
await shouldFail.reverting.withMessage(this.pausable.drasticMeasure({ from: other }),
'Pausable: not paused'
);
(await this.pausable.drasticMeasureTaken()).should.equal(false);
});
......@@ -41,7 +43,9 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('reverts when pausing from non-pauser', async function () {
await shouldFail.reverting(this.pausable.pause({ from: other }));
await shouldFail.reverting.withMessage(this.pausable.pause({ from: other }),
'PauserRole: caller does not have the Pauser role'
);
});
context('when paused', function () {
......@@ -54,7 +58,7 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('cannot perform normal process in pause', async function () {
await shouldFail.reverting(this.pausable.normalProcess({ from: other }));
await shouldFail.reverting.withMessage(this.pausable.normalProcess({ from: other }), 'Pausable: paused');
});
it('can take a drastic measure in a pause', async function () {
......@@ -63,7 +67,7 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('reverts when re-pausing', async function () {
await shouldFail.reverting(this.pausable.pause({ from: pauser }));
await shouldFail.reverting.withMessage(this.pausable.pause({ from: pauser }), 'Pausable: paused');
});
describe('unpausing', function () {
......@@ -73,7 +77,9 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('reverts when unpausing from non-pauser', async function () {
await shouldFail.reverting(this.pausable.unpause({ from: other }));
await shouldFail.reverting.withMessage(this.pausable.unpause({ from: other }),
'PauserRole: caller does not have the Pauser role'
);
});
context('when unpaused', function () {
......@@ -92,11 +98,13 @@ contract('Pausable', function ([_, pauser, otherPauser, other, ...otherAccounts]
});
it('should prevent drastic measure', async function () {
await shouldFail.reverting(this.pausable.drasticMeasure({ from: other }));
await shouldFail.reverting.withMessage(this.pausable.drasticMeasure({ from: other }),
'Pausable: not paused'
);
});
it('reverts when re-unpausing', async function () {
await shouldFail.reverting(this.pausable.unpause({ from: pauser }));
await shouldFail.reverting.withMessage(this.pausable.unpause({ from: pauser }), 'Pausable: not paused');
});
});
});
......
......@@ -13,9 +13,9 @@ contract('SafeMath', function () {
(await fn(rhs, lhs)).should.be.bignumber.equal(expected);
}
async function testFailsCommutative (fn, lhs, rhs) {
await shouldFail.reverting(fn(lhs, rhs));
await shouldFail.reverting(fn(rhs, lhs));
async function testFailsCommutative (fn, lhs, rhs, reason) {
await shouldFail.reverting.withMessage(fn(lhs, rhs), reason);
await shouldFail.reverting.withMessage(fn(rhs, lhs), reason);
}
describe('add', function () {
......@@ -30,7 +30,7 @@ contract('SafeMath', function () {
const a = MAX_UINT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.add, a, b);
await testFailsCommutative(this.safeMath.add, a, b, 'SafeMath: addition overflow');
});
});
......@@ -46,7 +46,7 @@ contract('SafeMath', function () {
const a = new BN('1234');
const b = new BN('5678');
await shouldFail.reverting(this.safeMath.sub(a, b));
await shouldFail.reverting.withMessage(this.safeMath.sub(a, b), 'SafeMath: subtraction overflow');
});
});
......@@ -69,7 +69,7 @@ contract('SafeMath', function () {
const a = MAX_UINT256;
const b = new BN('2');
await testFailsCommutative(this.safeMath.mul, a, b);
await testFailsCommutative(this.safeMath.mul, a, b, 'SafeMath: multiplication overflow');
});
});
......@@ -99,7 +99,7 @@ contract('SafeMath', function () {
const a = new BN('5678');
const b = new BN('0');
await shouldFail.reverting(this.safeMath.div(a, b));
await shouldFail.reverting.withMessage(this.safeMath.div(a, b), 'SafeMath: division by zero');
});
});
......@@ -138,7 +138,7 @@ contract('SafeMath', function () {
const a = new BN('5678');
const b = new BN('0');
await shouldFail.reverting(this.safeMath.mod(a, b));
await shouldFail.reverting.withMessage(this.safeMath.mod(a, b), 'SafeMath: modulo by zero');
});
});
});
......@@ -17,11 +17,17 @@ function shouldBehaveLikeOwnable (owner, [other]) {
});
it('should prevent non-owners from transferring', async function () {
await shouldFail.reverting(this.ownable.transferOwnership(other, { from: other }));
await shouldFail.reverting.withMessage(
this.ownable.transferOwnership(other, { from: other }),
'Ownable: caller is not the owner'
);
});
it('should guard ownership against stuck state', async function () {
await shouldFail.reverting(this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }));
await shouldFail.reverting.withMessage(
this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }),
'Ownable: new owner is the zero address'
);
});
it('loses owner after renouncement', async function () {
......@@ -32,7 +38,10 @@ function shouldBehaveLikeOwnable (owner, [other]) {
});
it('should prevent non-owners from renouncement', async function () {
await shouldFail.reverting(this.ownable.renounceOwnership({ from: other }));
await shouldFail.reverting.withMessage(
this.ownable.renounceOwnership({ from: other }),
'Ownable: caller is not the owner'
);
});
});
}
......
......@@ -18,7 +18,9 @@ contract('Secondary', function ([_, primary, newPrimary, other]) {
});
it('reverts when anyone calls onlyPrimary functions', async function () {
await shouldFail.reverting(this.secondary.onlyPrimaryMock({ from: other }));
await shouldFail.reverting.withMessage(this.secondary.onlyPrimaryMock({ from: other }),
'Secondary: caller is not the primary account'
);
});
});
......@@ -30,11 +32,15 @@ contract('Secondary', function ([_, primary, newPrimary, other]) {
});
it('reverts when transferring to the null address', async function () {
await shouldFail.reverting(this.secondary.transferPrimary(ZERO_ADDRESS, { from: primary }));
await shouldFail.reverting.withMessage(this.secondary.transferPrimary(ZERO_ADDRESS, { from: primary }),
'Secondary: new primary is the zero address'
);
});
it('reverts when called by anyone', async function () {
await shouldFail.reverting(this.secondary.transferPrimary(newPrimary, { from: other }));
await shouldFail.reverting.withMessage(this.secondary.transferPrimary(newPrimary, { from: other }),
'Secondary: caller is not the primary account'
);
});
context('with new primary', function () {
......@@ -47,7 +53,9 @@ contract('Secondary', function ([_, primary, newPrimary, other]) {
});
it('reverts when the old primary account calls onlyPrimary functions', async function () {
await shouldFail.reverting(this.secondary.onlyPrimaryMock({ from: primary }));
await shouldFail.reverting.withMessage(this.secondary.onlyPrimaryMock({ from: primary }),
'Secondary: caller is not the primary account'
);
});
});
});
......
......@@ -7,27 +7,37 @@ contract('PaymentSplitter', function ([_, owner, payee1, payee2, payee3, nonpaye
const amount = ether('1');
it('rejects an empty set of payees', async function () {
await shouldFail.reverting(PaymentSplitter.new([], []));
await shouldFail.reverting.withMessage(PaymentSplitter.new([], []), 'PaymentSplitter: no payees');
});
it('rejects more payees than shares', async function () {
await shouldFail.reverting(PaymentSplitter.new([payee1, payee2, payee3], [20, 30]));
await shouldFail.reverting.withMessage(PaymentSplitter.new([payee1, payee2, payee3], [20, 30]),
'PaymentSplitter: payees and shares length mismatch'
);
});
it('rejects more shares than payees', async function () {
await shouldFail.reverting(PaymentSplitter.new([payee1, payee2], [20, 30, 40]));
await shouldFail.reverting.withMessage(PaymentSplitter.new([payee1, payee2], [20, 30, 40]),
'PaymentSplitter: payees and shares length mismatch'
);
});
it('rejects null payees', async function () {
await shouldFail.reverting(PaymentSplitter.new([payee1, ZERO_ADDRESS], [20, 30]));
await shouldFail.reverting.withMessage(PaymentSplitter.new([payee1, ZERO_ADDRESS], [20, 30]),
'PaymentSplitter: account is the zero address'
);
});
it('rejects zero-valued shares', async function () {
await shouldFail.reverting(PaymentSplitter.new([payee1, payee2], [20, 0]));
await shouldFail.reverting.withMessage(PaymentSplitter.new([payee1, payee2], [20, 0]),
'PaymentSplitter: shares are 0'
);
});
it('rejects repeated payees', async function () {
await shouldFail.reverting(PaymentSplitter.new([payee1, payee1], [20, 30]));
await shouldFail.reverting.withMessage(PaymentSplitter.new([payee1, payee1], [20, 30]),
'PaymentSplitter: account already has shares'
);
});
context('once deployed', function () {
......@@ -64,12 +74,16 @@ contract('PaymentSplitter', function ([_, owner, payee1, payee2, payee3, nonpaye
});
it('should throw if no funds to claim', async function () {
await shouldFail.reverting(this.contract.release(payee1));
await shouldFail.reverting.withMessage(this.contract.release(payee1),
'PaymentSplitter: account is not due payment'
);
});
it('should throw if non-payee want to claim', async function () {
await send.ether(payer1, this.contract.address, amount);
await shouldFail.reverting(this.contract.release(nonpayee1));
await shouldFail.reverting.withMessage(this.contract.release(nonpayee1),
'PaymentSplitter: account has no shares'
);
});
it('should distribute funds to payees', async function () {
......
......@@ -26,7 +26,9 @@ contract('ConditionalEscrow', function ([_, owner, payee, ...otherAccounts]) {
it('reverts on withdrawals', async function () {
await this.escrow.deposit(payee, { from: owner, value: amount });
await shouldFail.reverting(this.escrow.withdraw(payee, { from: owner }));
await shouldFail.reverting.withMessage(this.escrow.withdraw(payee, { from: owner }),
'ConditionalEscrow: payee is not allowed to withdraw'
);
});
});
});
......@@ -18,7 +18,9 @@ function shouldBehaveLikeEscrow (primary, [payee1, payee2]) {
});
it('only the primary account can deposit', async function () {
await shouldFail.reverting(this.escrow.deposit(payee1, { from: payee2 }));
await shouldFail.reverting.withMessage(this.escrow.deposit(payee1, { from: payee2 }),
'Secondary: caller is not the primary account'
);
});
it('emits a deposited event', async function () {
......@@ -68,7 +70,9 @@ function shouldBehaveLikeEscrow (primary, [payee1, payee2]) {
});
it('only the primary account can withdraw', async function () {
await shouldFail.reverting(this.escrow.withdraw(payee1, { from: payee1 }));
await shouldFail.reverting.withMessage(this.escrow.withdraw(payee1, { from: payee1 }),
'Secondary: caller is not the primary account'
);
});
it('emits a withdrawn event', async function () {
......
......@@ -8,8 +8,8 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
const refundees = [refundee1, refundee2];
it('requires a non-null beneficiary', async function () {
await shouldFail.reverting(
RefundEscrow.new(ZERO_ADDRESS, { from: primary })
await shouldFail.reverting.withMessage(
RefundEscrow.new(ZERO_ADDRESS, { from: primary }), 'RefundEscrow: beneficiary is the zero address'
);
});
......@@ -32,17 +32,23 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
it('does not refund refundees', async function () {
await this.escrow.deposit(refundee1, { from: primary, value: amount });
await shouldFail.reverting(this.escrow.withdraw(refundee1));
await shouldFail.reverting.withMessage(this.escrow.withdraw(refundee1),
'ConditionalEscrow: payee is not allowed to withdraw'
);
});
it('does not allow beneficiary withdrawal', async function () {
await this.escrow.deposit(refundee1, { from: primary, value: amount });
await shouldFail.reverting(this.escrow.beneficiaryWithdraw());
await shouldFail.reverting.withMessage(this.escrow.beneficiaryWithdraw(),
'RefundEscrow: beneficiary can only withdraw while closed'
);
});
});
it('only the primary account can enter closed state', async function () {
await shouldFail.reverting(this.escrow.close({ from: beneficiary }));
await shouldFail.reverting.withMessage(this.escrow.close({ from: beneficiary }),
'Secondary: caller is not the primary account'
);
const { logs } = await this.escrow.close({ from: primary });
expectEvent.inLogs(logs, 'RefundsClosed');
......@@ -56,11 +62,15 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
});
it('rejects deposits', async function () {
await shouldFail.reverting(this.escrow.deposit(refundee1, { from: primary, value: amount }));
await shouldFail.reverting.withMessage(this.escrow.deposit(refundee1, { from: primary, value: amount }),
'RefundEscrow: can only deposit while active'
);
});
it('does not refund refundees', async function () {
await shouldFail.reverting(this.escrow.withdraw(refundee1));
await shouldFail.reverting.withMessage(this.escrow.withdraw(refundee1),
'ConditionalEscrow: payee is not allowed to withdraw'
);
});
it('allows beneficiary withdrawal', async function () {
......@@ -70,16 +80,22 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
});
it('prevents entering the refund state', async function () {
await shouldFail.reverting(this.escrow.enableRefunds({ from: primary }));
await shouldFail.reverting.withMessage(this.escrow.enableRefunds({ from: primary }),
'RefundEscrow: can only enable refunds while active'
);
});
it('prevents re-entering the closed state', async function () {
await shouldFail.reverting(this.escrow.close({ from: primary }));
await shouldFail.reverting.withMessage(this.escrow.close({ from: primary }),
'RefundEscrow: can only close while active'
);
});
});
it('only the primary account can enter refund state', async function () {
await shouldFail.reverting(this.escrow.enableRefunds({ from: beneficiary }));
await shouldFail.reverting.withMessage(this.escrow.enableRefunds({ from: beneficiary }),
'Secondary: caller is not the primary account'
);
const { logs } = await this.escrow.enableRefunds({ from: primary });
expectEvent.inLogs(logs, 'RefundsEnabled');
......@@ -93,7 +109,9 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
});
it('rejects deposits', async function () {
await shouldFail.reverting(this.escrow.deposit(refundee1, { from: primary, value: amount }));
await shouldFail.reverting.withMessage(this.escrow.deposit(refundee1, { from: primary, value: amount }),
'RefundEscrow: can only deposit while active'
);
});
it('refunds refundees', async function () {
......@@ -105,15 +123,21 @@ contract('RefundEscrow', function ([_, primary, beneficiary, refundee1, refundee
});
it('does not allow beneficiary withdrawal', async function () {
await shouldFail.reverting(this.escrow.beneficiaryWithdraw());
await shouldFail.reverting.withMessage(this.escrow.beneficiaryWithdraw(),
'RefundEscrow: beneficiary can only withdraw while closed'
);
});
it('prevents entering the closed state', async function () {
await shouldFail.reverting(this.escrow.close({ from: primary }));
await shouldFail.reverting.withMessage(this.escrow.close({ from: primary }),
'RefundEscrow: can only close while active'
);
});
it('prevents re-entering the refund state', async function () {
await shouldFail.reverting(this.escrow.enableRefunds({ from: primary }));
await shouldFail.reverting.withMessage(this.escrow.enableRefunds({ from: primary }),
'RefundEscrow: can only enable refunds while active'
);
});
});
});
......
......@@ -37,7 +37,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const amount = initialSupply.addn(1);
it('reverts', async function () {
await shouldFail.reverting(this.token.transfer(to, amount, { from: initialHolder }));
await shouldFail.reverting.withMessage(this.token.transfer(to, amount, { from: initialHolder }),
'SafeMath: subtraction overflow'
);
});
});
......@@ -68,7 +70,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const to = ZERO_ADDRESS;
it('reverts', async function () {
await shouldFail.reverting(this.token.transfer(to, initialSupply, { from: initialHolder }));
await shouldFail.reverting.withMessage(this.token.transfer(to, initialSupply, { from: initialHolder }),
'ERC20: transfer to the zero address'
);
});
});
});
......@@ -126,7 +130,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const amount = initialSupply.addn(1);
it('reverts', async function () {
await shouldFail.reverting(this.token.transferFrom(initialHolder, to, amount, { from: spender }));
await shouldFail.reverting.withMessage(this.token.transferFrom(
initialHolder, to, amount, { from: spender }), 'SafeMath: subtraction overflow'
);
});
});
});
......@@ -140,7 +146,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const amount = initialSupply;
it('reverts', async function () {
await shouldFail.reverting(this.token.transferFrom(initialHolder, to, amount, { from: spender }));
await shouldFail.reverting.withMessage(this.token.transferFrom(
initialHolder, to, amount, { from: spender }), 'SafeMath: subtraction overflow'
);
});
});
......@@ -148,7 +156,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const amount = initialSupply.addn(1);
it('reverts', async function () {
await shouldFail.reverting(this.token.transferFrom(initialHolder, to, amount, { from: spender }));
await shouldFail.reverting.withMessage(this.token.transferFrom(
initialHolder, to, amount, { from: spender }), 'SafeMath: subtraction overflow'
);
});
});
});
......@@ -163,7 +173,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
});
it('reverts', async function () {
await shouldFail.reverting(this.token.transferFrom(initialHolder, to, amount, { from: spender }));
await shouldFail.reverting.withMessage(this.token.transferFrom(
initialHolder, to, amount, { from: spender }), 'ERC20: transfer to the zero address'
);
});
});
});
......@@ -175,7 +187,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
function shouldDecreaseApproval (amount) {
describe('when there was no approved amount before', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.decreaseAllowance(spender, amount, { from: initialHolder }));
await shouldFail.reverting.withMessage(this.token.decreaseAllowance(
spender, amount, { from: initialHolder }), 'SafeMath: subtraction overflow'
);
});
});
......@@ -208,8 +222,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
});
it('reverts when more than the full allowance is removed', async function () {
await shouldFail.reverting(
this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder })
await shouldFail.reverting.withMessage(
this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }),
'SafeMath: subtraction overflow'
);
});
});
......@@ -233,7 +248,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const spender = ZERO_ADDRESS;
it('reverts', async function () {
await shouldFail.reverting(this.token.decreaseAllowance(spender, amount, { from: initialHolder }));
await shouldFail.reverting.withMessage(this.token.decreaseAllowance(
spender, amount, { from: initialHolder }, 'ERC20: approve to the zero address')
);
});
});
});
......@@ -315,16 +332,19 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
const spender = ZERO_ADDRESS;
it('reverts', async function () {
await shouldFail.reverting(this.token.increaseAllowance(spender, amount, { from: initialHolder }));
await shouldFail.reverting.withMessage(
this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'ERC20: approve to the zero address'
);
});
});
});
describe('_mint', function () {
const amount = new BN(50);
it('rejects a zero account', async function () {
await shouldFail.reverting(this.token.mint(ZERO_ADDRESS, amount));
it('rejects a null account', async function () {
await shouldFail.reverting.withMessage(
this.token.mint(ZERO_ADDRESS, amount), 'ERC20: mint to the zero address'
);
});
describe('for a non zero account', function () {
......@@ -354,13 +374,16 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
});
describe('_burn', function () {
it('rejects a zero account', async function () {
await shouldFail.reverting(this.token.burn(ZERO_ADDRESS, new BN(1)));
it('rejects a null account', async function () {
await shouldFail.reverting.withMessage(this.token.burn(ZERO_ADDRESS, new BN(1)),
'ERC20: burn from the zero address');
});
describe('for a non zero account', function () {
it('rejects burning more than balance', async function () {
await shouldFail.reverting(this.token.burn(initialHolder, initialSupply.addn(1)));
await shouldFail.reverting.withMessage(this.token.burn(
initialHolder, initialSupply.addn(1)), 'SafeMath: subtraction overflow'
);
});
const describeBurn = function (description, amount) {
......@@ -405,17 +428,23 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
await this.token.approve(spender, allowance, { from: initialHolder });
});
it('rejects a zero account', async function () {
await shouldFail.reverting(this.token.burnFrom(ZERO_ADDRESS, new BN(1)));
it('rejects a null account', async function () {
await shouldFail.reverting.withMessage(this.token.burnFrom(ZERO_ADDRESS, new BN(1)),
'ERC20: burn from the zero address'
);
});
describe('for a non zero account', function () {
it('rejects burning more than allowance', async function () {
await shouldFail.reverting(this.token.burnFrom(initialHolder, allowance.addn(1)));
await shouldFail.reverting.withMessage(this.token.burnFrom(initialHolder, allowance.addn(1)),
'SafeMath: subtraction overflow'
);
});
it('rejects burning more than balance', async function () {
await shouldFail.reverting(this.token.burnFrom(initialHolder, initialSupply.addn(1)));
await shouldFail.reverting.withMessage(this.token.burnFrom(initialHolder, initialSupply.addn(1)),
'SafeMath: subtraction overflow'
);
});
const describeBurnFrom = function (description, amount) {
......@@ -477,7 +506,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
describe('when the owner is the zero address', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply));
await shouldFail.reverting.withMessage(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply),
'ERC20: approve from the zero address'
);
});
});
});
......@@ -555,7 +586,9 @@ contract('ERC20', function ([_, initialHolder, recipient, anotherAccount]) {
describe('when the spender is the zero address', function () {
it('reverts', async function () {
await shouldFail.reverting(approve.call(this, owner, ZERO_ADDRESS, supply));
await shouldFail.reverting.withMessage(approve.call(this, owner, ZERO_ADDRESS, supply),
'ERC20: approve to the zero address'
);
});
});
}
......
......@@ -8,8 +8,8 @@ contract('ERC20Capped', function ([_, minter, ...otherAccounts]) {
const cap = ether('1000');
it('requires a non-zero cap', async function () {
await shouldFail.reverting(
ERC20Capped.new(new BN(0), { from: minter })
await shouldFail.reverting.withMessage(
ERC20Capped.new(new BN(0), { from: minter }), 'ERC20Capped: cap is 0'
);
});
......
......@@ -42,7 +42,7 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
});
it('reverts', async function () {
await shouldFail.reverting(this.token.pause({ from }));
await shouldFail.reverting.withMessage(this.token.pause({ from }), 'Pausable: paused');
});
});
});
......@@ -51,7 +51,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
const from = anotherAccount;
it('reverts', async function () {
await shouldFail.reverting(this.token.pause({ from }));
await shouldFail.reverting.withMessage(this.token.pause({ from }),
'PauserRole: caller does not have the Pauser role'
);
});
});
});
......@@ -79,7 +81,7 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
describe('when the token is unpaused', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.unpause({ from }));
await shouldFail.reverting.withMessage(this.token.unpause({ from }), 'Pausable: not paused');
});
});
});
......@@ -88,7 +90,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
const from = anotherAccount;
it('reverts', async function () {
await shouldFail.reverting(this.token.unpause({ from }));
await shouldFail.reverting.withMessage(this.token.unpause({ from }),
'PauserRole: caller does not have the Pauser role'
);
});
});
});
......@@ -134,7 +138,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
it('reverts when trying to transfer when paused', async function () {
await this.token.pause({ from: pauser });
await shouldFail.reverting(this.token.transfer(recipient, initialSupply, { from: pauser }));
await shouldFail.reverting.withMessage(this.token.transfer(recipient, initialSupply, { from: pauser }),
'Pausable: paused'
);
});
});
......@@ -159,7 +165,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
it('reverts when trying to approve when paused', async function () {
await this.token.pause({ from: pauser });
await shouldFail.reverting(this.token.approve(anotherAccount, allowance, { from: pauser }));
await shouldFail.reverting.withMessage(this.token.approve(anotherAccount, allowance, { from: pauser }),
'Pausable: paused'
);
});
});
......@@ -190,7 +198,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
it('reverts when trying to transfer from when paused', async function () {
await this.token.pause({ from: pauser });
await shouldFail.reverting(this.token.transferFrom(pauser, recipient, allowance, { from: anotherAccount }));
await shouldFail.reverting.withMessage(this.token.transferFrom(
pauser, recipient, allowance, { from: anotherAccount }), 'Pausable: paused'
);
});
});
......@@ -220,7 +230,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
it('reverts when trying to transfer when paused', async function () {
await this.token.pause({ from: pauser });
await shouldFail.reverting(this.token.decreaseAllowance(anotherAccount, decrement, { from: pauser }));
await shouldFail.reverting.withMessage(this.token.decreaseAllowance(
anotherAccount, decrement, { from: pauser }), 'Pausable: paused'
);
});
});
......@@ -250,7 +262,9 @@ contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherA
it('reverts when trying to increase approval when paused', async function () {
await this.token.pause({ from: pauser });
await shouldFail.reverting(this.token.increaseAllowance(anotherAccount, increment, { from: pauser }));
await shouldFail.reverting.withMessage(this.token.increaseAllowance(
anotherAccount, increment, { from: pauser }), 'Pausable: paused'
);
});
});
});
......
......@@ -11,7 +11,7 @@ contract('SafeERC20', function ([_, hasNoCode]) {
this.wrapper = await SafeERC20Wrapper.new(hasNoCode);
});
shouldRevertOnAllCalls();
shouldRevertOnAllCalls('SafeERC20: call to non-contract');
});
describe('with token that returns false on all calls', function () {
......@@ -19,7 +19,7 @@ contract('SafeERC20', function ([_, hasNoCode]) {
this.wrapper = await SafeERC20Wrapper.new((await ERC20ReturnFalseMock.new()).address);
});
shouldRevertOnAllCalls();
shouldRevertOnAllCalls('SafeERC20: ERC20 operation did not succeed');
});
describe('with token that returns true on all calls', function () {
......@@ -39,24 +39,26 @@ contract('SafeERC20', function ([_, hasNoCode]) {
});
});
function shouldRevertOnAllCalls () {
function shouldRevertOnAllCalls (reason) {
it('reverts on transfer', async function () {
await shouldFail.reverting(this.wrapper.transfer());
await shouldFail.reverting.withMessage(this.wrapper.transfer(), reason);
});
it('reverts on transferFrom', async function () {
await shouldFail.reverting(this.wrapper.transferFrom());
await shouldFail.reverting.withMessage(this.wrapper.transferFrom(), reason);
});
it('reverts on approve', async function () {
await shouldFail.reverting(this.wrapper.approve(0));
await shouldFail.reverting.withMessage(this.wrapper.approve(0), reason);
});
it('reverts on increaseAllowance', async function () {
// [TODO] make sure it's reverting for the right reason
await shouldFail.reverting(this.wrapper.increaseAllowance(0));
});
it('reverts on decreaseAllowance', async function () {
// [TODO] make sure it's reverting for the right reason
await shouldFail.reverting(this.wrapper.decreaseAllowance(0));
});
}
......@@ -89,7 +91,10 @@ function shouldOnlyRevertOnErrors () {
});
it('reverts when decreasing the allowance', async function () {
await shouldFail.reverting(this.wrapper.decreaseAllowance(10));
await shouldFail.reverting.withMessage(
this.wrapper.decreaseAllowance(10),
'SafeMath: subtraction overflow'
);
});
});
......@@ -99,7 +104,10 @@ function shouldOnlyRevertOnErrors () {
});
it('reverts when approving a non-zero allowance', async function () {
await shouldFail.reverting(this.wrapper.approve(20));
await shouldFail.reverting.withMessage(
this.wrapper.approve(20),
'SafeERC20: approve from non-zero to non-zero allowance'
);
});
it('doesn\'t revert when approving a zero allowance', async function () {
......@@ -115,7 +123,10 @@ function shouldOnlyRevertOnErrors () {
});
it('reverts when decreasing the allowance to a negative value', async function () {
await shouldFail.reverting(this.wrapper.decreaseAllowance(200));
await shouldFail.reverting.withMessage(
this.wrapper.decreaseAllowance(200),
'SafeMath: subtraction overflow'
);
});
});
});
......
......@@ -13,7 +13,7 @@ contract('TokenTimelock', function ([_, minter, beneficiary]) {
it('rejects a release time in the past', async function () {
const pastReleaseTime = (await time.latest()).sub(time.duration.years(1));
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
TokenTimelock.new(this.token.address, beneficiary, pastReleaseTime)
);
});
......@@ -32,12 +32,12 @@ contract('TokenTimelock', function ([_, minter, beneficiary]) {
});
it('cannot be released before time limit', async function () {
await shouldFail.reverting(this.timelock.release());
await shouldFail.reverting.withMessage(this.timelock.release());
});
it('cannot be released just before time limit', async function () {
await time.increaseTo(this.releaseTime.sub(time.duration.seconds(3)));
await shouldFail.reverting(this.timelock.release());
await shouldFail.reverting.withMessage(this.timelock.release());
});
it('can be released just after limit', async function () {
......@@ -55,7 +55,7 @@ contract('TokenTimelock', function ([_, minter, beneficiary]) {
it('cannot be released twice', async function () {
await time.increaseTo(this.releaseTime.add(time.duration.years(1)));
await this.timelock.release();
await shouldFail.reverting(this.timelock.release());
await shouldFail.reverting.withMessage(this.timelock.release());
(await this.token.balanceOf(beneficiary)).should.be.bignumber.equal(amount);
});
});
......
......@@ -35,7 +35,9 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
const amount = initialBalance.addn(1);
it('reverts', async function () {
await shouldFail.reverting(this.token.burn(amount, { from: owner }));
await shouldFail.reverting.withMessage(this.token.burn(amount, { from: owner }),
'SafeMath: subtraction overflow'
);
});
});
});
......@@ -82,7 +84,9 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await this.token.approve(burner, amount, { from: owner });
await shouldFail.reverting(this.token.burnFrom(owner, amount, { from: burner }));
await shouldFail.reverting.withMessage(this.token.burnFrom(owner, amount, { from: burner }),
'SafeMath: subtraction overflow'
);
});
});
......@@ -91,7 +95,9 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await this.token.approve(burner, allowance, { from: owner });
await shouldFail.reverting(this.token.burnFrom(owner, allowance.addn(1), { from: burner }));
await shouldFail.reverting.withMessage(this.token.burnFrom(owner, allowance.addn(1), { from: burner }),
'SafeMath: subtraction overflow'
);
});
});
});
......
......@@ -15,12 +15,12 @@ function shouldBehaveLikeERC20Capped (minter, [other], cap) {
it('should fail to mint if the amount exceeds the cap', async function () {
await this.token.mint(other, cap.subn(1), { from });
await shouldFail.reverting(this.token.mint(other, 2, { from }));
await shouldFail.reverting.withMessage(this.token.mint(other, 2, { from }));
});
it('should fail to mint after cap is reached', async function () {
await this.token.mint(other, cap, { from });
await shouldFail.reverting(this.token.mint(other, 1, { from }));
await shouldFail.reverting.withMessage(this.token.mint(other, 1, { from }));
});
});
}
......
......@@ -40,7 +40,9 @@ function shouldBehaveLikeERC20Mintable (minter, [other]) {
const from = other;
it('reverts', async function () {
await shouldFail.reverting(this.token.mint(other, amount, { from }));
await shouldFail.reverting.withMessage(this.token.mint(other, amount, { from }),
'MinterRole: caller does not have the Minter role'
);
});
});
});
......
......@@ -36,7 +36,9 @@ function shouldBehaveLikeERC721 (
context('when querying the zero address', function () {
it('throws', async function () {
await shouldFail.reverting(this.token.balanceOf(ZERO_ADDRESS));
await shouldFail.reverting.withMessage(
this.token.balanceOf(ZERO_ADDRESS), 'ERC721: balance query for the zero address'
);
});
});
});
......@@ -54,7 +56,9 @@ function shouldBehaveLikeERC721 (
const tokenId = unknownTokenId;
it('reverts', async function () {
await shouldFail.reverting(this.token.ownerOf(tokenId));
await shouldFail.reverting.withMessage(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token'
);
});
});
});
......@@ -178,29 +182,36 @@ function shouldBehaveLikeERC721 (
context('when the address of the previous owner is incorrect', function () {
it('reverts', async function () {
await shouldFail.reverting(transferFunction.call(this, other, other, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
transferFunction.call(this, other, other, tokenId, { from: owner }),
'ERC721: transfer of token that is not own'
);
});
});
context('when the sender is not authorized for the token id', function () {
it('reverts', async function () {
await shouldFail.reverting(transferFunction.call(this, owner, other, tokenId, { from: other })
await shouldFail.reverting.withMessage(
transferFunction.call(this, owner, other, tokenId, { from: other }),
'ERC721: transfer caller is not owner nor approved'
);
});
});
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await shouldFail.reverting(transferFunction.call(this, owner, other, unknownTokenId, { from: owner })
await shouldFail.reverting.withMessage(
transferFunction.call(this, owner, other, unknownTokenId, { from: owner }),
'ERC721: operator query for nonexistent token'
);
});
});
context('when the address to transfer the token to is the zero address', function () {
it('reverts', async function () {
await shouldFail.reverting(
transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner }),
'ERC721: transfer to the zero address'
);
});
});
......@@ -258,14 +269,15 @@ function shouldBehaveLikeERC721 (
describe('with an invalid token id', function () {
it('reverts', async function () {
await shouldFail.reverting(
await shouldFail.reverting.withMessage(
transferFun.call(
this,
owner,
this.receiver.address,
unknownTokenId,
{ from: owner },
)
),
'ERC721: operator query for nonexistent token'
);
});
});
......@@ -283,8 +295,9 @@ function shouldBehaveLikeERC721 (
describe('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
await shouldFail.reverting(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer'
);
});
});
......@@ -292,8 +305,9 @@ function shouldBehaveLikeERC721 (
describe('to a receiver contract that throws', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await shouldFail.reverting(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting'
);
});
});
......@@ -301,8 +315,9 @@ function shouldBehaveLikeERC721 (
describe('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const invalidReceiver = this.token;
await shouldFail.reverting(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'VM Exception while processing transaction: revert'
);
});
});
......@@ -390,22 +405,24 @@ function shouldBehaveLikeERC721 (
context('when the address that receives the approval is the owner', function () {
it('reverts', async function () {
await shouldFail.reverting(
this.token.approve(owner, tokenId, { from: owner })
await shouldFail.reverting.withMessage(
this.token.approve(owner, tokenId, { from: owner }), 'ERC721: transfer to current owner'
);
});
});
context('when the sender does not own the given token ID', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.approve(approved, tokenId, { from: other }));
await shouldFail.reverting.withMessage(this.token.approve(approved, tokenId, { from: other }),
'ERC721: approve caller is not owner nor approved');
});
});
context('when the sender is approved for the given token ID', function () {
it('reverts', async function () {
await this.token.approve(approved, tokenId, { from: owner });
await shouldFail.reverting(this.token.approve(anotherApproved, tokenId, { from: approved }));
await shouldFail.reverting.withMessage(this.token.approve(anotherApproved, tokenId, { from: approved }),
'ERC721: approve caller is not owner nor approved for all');
});
});
......@@ -421,7 +438,8 @@ function shouldBehaveLikeERC721 (
context('when the given token ID does not exist', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.approve(approved, unknownTokenId, { from: operator }));
await shouldFail.reverting.withMessage(this.token.approve(approved, unknownTokenId, { from: operator }),
'ERC721: owner query for nonexistent token');
});
});
});
......@@ -499,7 +517,8 @@ function shouldBehaveLikeERC721 (
context('when the operator is the owner', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.setApprovalForAll(owner, true, { from: owner }));
await shouldFail.reverting.withMessage(this.token.setApprovalForAll(owner, true, { from: owner }),
'ERC721: approve to caller');
});
});
});
......
......@@ -15,8 +15,10 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
const tokenId = new BN('5042');
describe('_mint(address, uint256)', function () {
it('reverts with a zero destination address', async function () {
await shouldFail.reverting(this.token.mint(ZERO_ADDRESS, tokenId));
it('reverts with a null destination address', async function () {
await shouldFail.reverting.withMessage(
this.token.mint(ZERO_ADDRESS, tokenId), 'ERC721: mint to the zero address'
);
});
context('with minted token', async function () {
......@@ -34,14 +36,16 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
});
it('reverts when adding a token id that already exists', async function () {
await shouldFail.reverting(this.token.mint(tokenOwner, tokenId));
await shouldFail.reverting.withMessage(this.token.mint(tokenOwner, tokenId), 'ERC721: token already minted');
});
});
});
describe('_burn(address, uint256)', function () {
it('reverts when burning a non-existent token id', async function () {
await shouldFail.reverting(this.token.methods['burn(address,uint256)'](tokenOwner, tokenId));
await shouldFail.reverting.withMessage(
this.token.methods['burn(address,uint256)'](tokenOwner, tokenId), 'ERC721: owner query for nonexistent token'
);
});
context('with minted token', function () {
......@@ -50,7 +54,9 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
});
it('reverts when the account is not the owner', async function () {
await shouldFail.reverting(this.token.methods['burn(address,uint256)'](other, tokenId));
await shouldFail.reverting.withMessage(
this.token.methods['burn(address,uint256)'](other, tokenId), 'ERC721: burn of token that is not own'
);
});
context('with burnt token', function () {
......@@ -64,11 +70,16 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
it('deletes the token', async function () {
(await this.token.balanceOf(tokenOwner)).should.be.bignumber.equal('0');
await shouldFail.reverting(this.token.ownerOf(tokenId));
await shouldFail.reverting.withMessage(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token'
);
});
it('reverts when burning a token id that has been deleted', async function () {
await shouldFail.reverting(this.token.methods['burn(address,uint256)'](tokenOwner, tokenId));
await shouldFail.reverting.withMessage(
this.token.methods['burn(address,uint256)'](tokenOwner, tokenId),
'ERC721: owner query for nonexistent token'
);
});
});
});
......@@ -76,7 +87,9 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
describe('_burn(uint256)', function () {
it('reverts when burning a non-existent token id', async function () {
await shouldFail.reverting(this.token.methods['burn(uint256)'](tokenId));
await shouldFail.reverting.withMessage(
this.token.methods['burn(uint256)'](tokenId), 'ERC721: owner query for nonexistent token'
);
});
context('with minted token', function () {
......@@ -95,11 +108,15 @@ contract('ERC721', function ([_, creator, tokenOwner, other, ...accounts]) {
it('deletes the token', async function () {
(await this.token.balanceOf(tokenOwner)).should.be.bignumber.equal('0');
await shouldFail.reverting(this.token.ownerOf(tokenId));
await shouldFail.reverting.withMessage(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token'
);
});
it('reverts when burning a token id that has been deleted', async function () {
await shouldFail.reverting(this.token.methods['burn(uint256)'](tokenId));
await shouldFail.reverting.withMessage(
this.token.methods['burn(uint256)'](tokenId), 'ERC721: owner query for nonexistent token'
);
});
});
});
......
......@@ -64,7 +64,9 @@ contract('ERC721Full', function ([
it('burns all tokens', async function () {
await this.token.burn(secondTokenId, { from: owner });
(await this.token.totalSupply()).should.be.bignumber.equal('0');
await shouldFail.reverting(this.token.tokenByIndex(0));
await shouldFail.reverting.withMessage(
this.token.tokenByIndex(0), 'ERC721Enumerable: global index out of bounds'
);
});
});
......@@ -85,7 +87,9 @@ contract('ERC721Full', function ([
});
it('reverts when setting metadata for non existent token id', async function () {
await shouldFail.reverting(this.token.setTokenURI(nonExistentTokenId, sampleUri));
await shouldFail.reverting.withMessage(
this.token.setTokenURI(nonExistentTokenId, sampleUri), 'ERC721Metadata: URI set of nonexistent token'
);
});
it('can burn token with metadata', async function () {
......@@ -99,7 +103,9 @@ contract('ERC721Full', function ([
});
it('reverts when querying metadata for non existent token id', async function () {
await shouldFail.reverting(this.token.tokenURI(nonExistentTokenId));
await shouldFail.reverting.withMessage(
this.token.tokenURI(nonExistentTokenId), 'ERC721Metadata: URI query for nonexistent token'
);
});
});
......@@ -127,13 +133,17 @@ contract('ERC721Full', function ([
describe('when the index is greater than or equal to the total tokens owned by the given address', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.tokenOfOwnerByIndex(owner, 2));
await shouldFail.reverting.withMessage(
this.token.tokenOfOwnerByIndex(owner, 2), 'ERC721Enumerable: owner index out of bounds'
);
});
});
describe('when the given address does not own any token', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.tokenOfOwnerByIndex(another, 0));
await shouldFail.reverting.withMessage(
this.token.tokenOfOwnerByIndex(another, 0), 'ERC721Enumerable: owner index out of bounds'
);
});
});
......@@ -153,7 +163,9 @@ contract('ERC721Full', function ([
it('returns empty collection for original owner', async function () {
(await this.token.balanceOf(owner)).should.be.bignumber.equal('0');
await shouldFail.reverting(this.token.tokenOfOwnerByIndex(owner, 0));
await shouldFail.reverting.withMessage(
this.token.tokenOfOwnerByIndex(owner, 0), 'ERC721Enumerable: owner index out of bounds'
);
});
});
});
......@@ -167,7 +179,9 @@ contract('ERC721Full', function ([
});
it('should revert if index is greater than supply', async function () {
await shouldFail.reverting(this.token.tokenByIndex(2));
await shouldFail.reverting.withMessage(
this.token.tokenByIndex(2), 'ERC721Enumerable: global index out of bounds'
);
});
[firstTokenId, secondTokenId].forEach(function (tokenId) {
......
......@@ -46,13 +46,18 @@ function shouldBehaveLikeMintAndBurnERC721 (
describe('when the given owner address is the zero address', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.mint(ZERO_ADDRESS, thirdTokenId, { from: minter }));
await shouldFail.reverting.withMessage(
this.token.mint(ZERO_ADDRESS, thirdTokenId, { from: minter }),
'ERC721: mint to the zero address'
);
});
});
describe('when the given token ID was already tracked by this contract', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.mint(owner, firstTokenId, { from: minter }));
await shouldFail.reverting.withMessage(this.token.mint(owner, firstTokenId, { from: minter }),
'ERC721: token already minted.'
);
});
});
});
......@@ -76,7 +81,10 @@ function shouldBehaveLikeMintAndBurnERC721 (
});
it('burns the given token ID and adjusts the balance of the owner', async function () {
await shouldFail.reverting(this.token.ownerOf(tokenId));
await shouldFail.reverting.withMessage(
this.token.ownerOf(tokenId),
'ERC721: owner query for nonexistent token'
);
(await this.token.balanceOf(owner)).should.be.bignumber.equal('1');
});
......@@ -98,15 +106,17 @@ function shouldBehaveLikeMintAndBurnERC721 (
context('getApproved', function () {
it('reverts', async function () {
await shouldFail.reverting(this.token.getApproved(tokenId));
await shouldFail.reverting.withMessage(
this.token.getApproved(tokenId), 'ERC721: approved query for nonexistent token'
);
});
});
});
describe('when the given token ID was not tracked by this contract', function () {
it('reverts', async function () {
await shouldFail.reverting(
this.token.burn(unknownTokenId, { from: creator })
await shouldFail.reverting.withMessage(
this.token.burn(unknownTokenId, { from: creator }), 'ERC721: operator query for nonexistent token'
);
});
});
......
......@@ -12,24 +12,34 @@ function shouldBehaveLikeERC721PausedToken (owner, [recipient, operator]) {
});
it('reverts when trying to approve', async function () {
await shouldFail.reverting(this.token.approve(recipient, firstTokenId, { from: owner }));
await shouldFail.reverting.withMessage(
this.token.approve(recipient, firstTokenId, { from: owner }), 'Pausable: paused'
);
});
it('reverts when trying to setApprovalForAll', async function () {
await shouldFail.reverting(this.token.setApprovalForAll(operator, true, { from: owner }));
await shouldFail.reverting.withMessage(
this.token.setApprovalForAll(operator, true, { from: owner }), 'Pausable: paused'
);
});
it('reverts when trying to transferFrom', async function () {
await shouldFail.reverting(this.token.transferFrom(owner, recipient, firstTokenId, { from: owner }));
await shouldFail.reverting.withMessage(
this.token.transferFrom(owner, recipient, firstTokenId, { from: owner }), 'Pausable: paused'
);
});
it('reverts when trying to safeTransferFrom', async function () {
await shouldFail.reverting(this.token.safeTransferFrom(owner, recipient, firstTokenId, { from: owner }));
await shouldFail.reverting.withMessage(
this.token.safeTransferFrom(owner, recipient, firstTokenId, { from: owner }), 'Pausable: paused'
);
});
it('reverts when trying to safeTransferFrom with data', async function () {
await shouldFail.reverting(this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](
owner, recipient, firstTokenId, mockData, { from: owner })
await shouldFail.reverting.withMessage(
this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](
owner, recipient, firstTokenId, mockData, { from: owner }
), 'Pausable: paused'
);
});
......
......@@ -11,7 +11,8 @@ contract('ReentrancyGuard', function () {
it('should not allow remote callback', async function () {
const attacker = await ReentrancyAttack.new();
await shouldFail.reverting(this.reentrancyMock.countAndCall(attacker.address));
await shouldFail.reverting.withMessage(
this.reentrancyMock.countAndCall(attacker.address), 'ReentrancyGuard: reentrant call');
});
// The following are more side-effects than intended behavior:
......@@ -19,10 +20,14 @@ contract('ReentrancyGuard', function () {
// in the side-effects.
it('should not allow local recursion', async function () {
await shouldFail.reverting(this.reentrancyMock.countLocalRecursive(10));
await shouldFail.reverting.withMessage(
this.reentrancyMock.countLocalRecursive(10), 'ReentrancyGuard: reentrant call'
);
});
it('should not allow indirect local recursion', async function () {
await shouldFail.reverting(this.reentrancyMock.countThisRecursive(10));
await shouldFail.reverting.withMessage(
this.reentrancyMock.countThisRecursive(10), 'ReentrancyMock: failed call'
);
});
});
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