Merge branch 'master' into solc-0.7

release-v3.3-solc-0.7^2
Francisco Giordano 4 years ago
commit 4972bf4f23
  1. 84
      .circleci/config.yml
  2. 2
      .eslintrc
  3. 45
      .github/workflows/test.yml
  4. 7
      .gitignore
  5. 5
      .solcover.js
  6. 9
      CHANGELOG.md
  7. 7
      CONTRIBUTING.md
  8. 3
      README.md
  9. 20
      buidler.config.js
  10. 10
      buidler/env-contract.js
  11. 81
      contracts/access/README.adoc
  12. 281
      contracts/access/TimelockController.sol
  13. 9
      contracts/cryptography/ECDSA.sol
  14. 12
      contracts/mocks/AddressImpl.sol
  15. 10
      contracts/mocks/CallReceiverMock.sol
  16. 2
      contracts/mocks/Create2Impl.sol
  17. 6
      contracts/mocks/DummyImplementation.sol
  18. 33
      contracts/mocks/EnumerableSetMock.sol
  19. 2
      contracts/package.json
  20. 4
      contracts/proxy/Proxy.sol
  21. 2
      contracts/proxy/TransparentUpgradeableProxy.sol
  22. 9
      contracts/token/ERC20/ERC20.sol
  23. 3
      contracts/token/ERC721/IERC721Receiver.sol
  24. 72
      contracts/utils/Address.sol
  25. 58
      contracts/utils/EnumerableSet.sol
  26. 8
      docs/modules/ROOT/nav.adoc
  27. 34
      docs/modules/ROOT/pages/access-control.adoc
  28. 2
      docs/modules/ROOT/pages/erc1155.adoc
  29. 2
      docs/modules/ROOT/pages/index.adoc
  30. 71
      docs/modules/ROOT/pages/upgradeable.adoc
  31. 25
      package.json
  32. 5
      renovate.json
  33. 9
      scripts/compile.sh
  34. 25
      scripts/coverage.sh
  35. 2
      scripts/gen-nav.js
  36. 10
      scripts/prepack.sh
  37. 0
      scripts/prepare-contracts-package.sh
  38. 11
      scripts/prepare-docs-solc.js
  39. 9
      scripts/prepare-docs.sh
  40. 2
      scripts/release/update-changelog-release-date.js
  41. 15
      scripts/remove-ignored-artifacts.js
  42. 22
      test-environment.config.js
  43. 4
      test/GSN/Context.behavior.js
  44. 8
      test/GSN/Context.test.js
  45. 17
      test/GSN/ERC721GSNRecipientMock.test.js
  46. 18
      test/GSN/GSNRecipient.test.js
  47. 17
      test/GSN/GSNRecipientERC20Fee.test.js
  48. 33
      test/GSN/GSNRecipientSignature.test.js
  49. 2
      test/TESTING.md
  50. 16
      test/access/AccessControl.test.js
  51. 11
      test/access/Ownable.test.js
  52. 992
      test/access/TimelockController.test.js
  53. 10
      test/cryptography/ECDSA.test.js
  54. 6
      test/cryptography/MerkleProof.test.js
  55. 15
      test/helpers/set-gsn-provider.js
  56. 4
      test/helpers/sign.js
  57. 5
      test/introspection/ERC165.test.js
  58. 9
      test/introspection/ERC165Checker.test.js
  59. 12
      test/introspection/ERC1820Implementer.test.js
  60. 6
      test/introspection/SupportsInterface.behavior.js
  61. 5
      test/math/Math.test.js
  62. 5
      test/math/SafeMath.test.js
  63. 6
      test/math/SignedSafeMath.test.js
  64. 20
      test/payment/PaymentSplitter.test.js
  65. 6
      test/payment/PullPayment.test.js
  66. 8
      test/payment/escrow/ConditionalEscrow.test.js
  67. 4
      test/payment/escrow/Escrow.behavior.js
  68. 6
      test/payment/escrow/Escrow.test.js
  69. 32
      test/payment/escrow/RefundEscrow.test.js
  70. 24
      test/presets/ERC1155PresetMinterPauser.test.js
  71. 10
      test/presets/ERC20PresetMinterPauser.test.js
  72. 12
      test/presets/ERC721PresetMinterPauserAutoId.test.js
  73. 8
      test/proxy/Initializable.test.js
  74. 18
      test/proxy/ProxyAdmin.test.js
  75. 30
      test/proxy/TransparentUpgradeableProxy.behaviour.js
  76. 6
      test/proxy/TransparentUpgradeableProxy.test.js
  77. 4
      test/proxy/UpgradeableProxy.behaviour.js
  78. 6
      test/proxy/UpgradeableProxy.test.js
  79. 2
      test/setup.js
  80. 90
      test/token/ERC1155/ERC1155.behavior.js
  81. 42
      test/token/ERC1155/ERC1155.test.js
  82. 10
      test/token/ERC1155/ERC1155Burnable.test.js
  83. 11
      test/token/ERC1155/ERC1155Holder.test.js
  84. 24
      test/token/ERC1155/ERC1155Pausable.test.js
  85. 20
      test/token/ERC20/ERC20.behavior.js
  86. 24
      test/token/ERC20/ERC20.test.js
  87. 6
      test/token/ERC20/ERC20Burnable.test.js
  88. 8
      test/token/ERC20/ERC20Capped.test.js
  89. 14
      test/token/ERC20/ERC20Pausable.test.js
  90. 20
      test/token/ERC20/ERC20Snapshot.test.js
  91. 18
      test/token/ERC20/SafeERC20.test.js
  92. 10
      test/token/ERC20/TokenTimelock.test.js
  93. 6
      test/token/ERC20/behaviors/ERC20Burnable.behavior.js
  94. 76
      test/token/ERC721/ERC721.test.js
  95. 12
      test/token/ERC721/ERC721Burnable.test.js
  96. 8
      test/token/ERC721/ERC721Holder.test.js
  97. 18
      test/token/ERC721/ERC721Pausable.test.js
  98. 39
      test/token/ERC777/ERC777.behavior.js
  99. 16
      test/token/ERC777/ERC777.test.js
  100. 135
      test/utils/Address.test.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,84 +0,0 @@
version: 2
# 2.1 does not yet support local run
# unless with workaround. For simplicity just use it.
# https://github.com/CircleCI-Public/circleci-cli/issues/79
aliases:
- &defaults
docker:
- image: circleci/node:10
- &cache_key_node_modules
key: v1-node_modules-{{ checksum "package-lock.json" }}
jobs:
dependencies:
<<: *defaults
steps:
- checkout
- restore_cache:
<<: *cache_key_node_modules
- run:
name: Install npm dependencies and prepare
command: |
if [ ! -d node_modules ]; then
npm ci
else
npm run prepare
fi
- persist_to_workspace:
root: .
paths:
- node_modules
- build
- save_cache:
paths:
- node_modules
<<: *cache_key_node_modules
lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Linter
command: npm run lint
test:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Unit tests
command: npm run test
coverage:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Unit tests with coverage report
command: npm run coverage
# TODO(xinbenlv, #1839): run SOLC_NIGHTLY to be run but allow it to fail.
workflows:
version: 2
everything:
jobs:
- dependencies
- lint:
requires:
- dependencies
- test:
requires:
- dependencies
- coverage:
requires:
- dependencies

@ -18,6 +18,8 @@
"contract": false,
"assert": false,
"web3": false,
"usePlugin": false,
"extendEnvironment": false,
},
"rules": {

@ -0,0 +1,45 @@
name: Test
on:
push:
branches:
- master
- release-v*
pull_request: {}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10.x
- uses: actions/cache@v2
id: cache
with:
path: '**/node_modules'
key: npm-v2-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-v2-
- run: npm ci
if: steps.cache.outputs.cache-hit != 'true'
- run: npm run lint
- run: npm run test
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10.x
- uses: actions/cache@v2
id: cache
with:
path: '**/node_modules'
key: npm-v2-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-v2-
- run: npm ci
if: steps.cache.outputs.cache-hit != 'true'
- run: npm run coverage
- uses: codecov/codecov-action@v1

7
.gitignore vendored

@ -50,3 +50,10 @@ contracts/README.md
# temporary artifact from solidity-coverage
allFiredEvents
.coverage_artifacts
.coverage_cache
.coverage_contracts
# buidler
cache
artifacts

@ -4,5 +4,8 @@ module.exports = {
compileCommand: 'npm run compile',
skipFiles: [
'mocks',
]
],
providerOptions: {
default_balance_ether: '10000000000000000000000000',
},
}

@ -1,11 +1,16 @@
# Changelog
## Unreleased
* `Address`: added `functionStaticCall` and `functionDelegateCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333))
* `TimelockController`: added a contract to augment access control schemes with a delay. ([#2364](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2364))
* `EnumerableSet`: added `BytesSet`, for sets of `bytes32`. ([#2395](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2395))
## 3.2.2-solc-0.7 (2020-10-28)
* Resolve warnings introduced by Solidity 0.7.4. ([#2396](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2396))
## 3.2.1-solc-0.7 (2020-09-15)
* `ERC777`: Remove a warning about function state visibility in Solidity 0.7. ([#2327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2327))
* `ERC777`: Remove a warning about function state visibility in Solidity 0.7. ([#2327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2327))
## 3.2.0 (2020-09-10)

@ -44,13 +44,6 @@ npm test
npm run lint
```
or you can simply run CircleCI locally
```bash
circleci local execute --job build
circleci local execute --job test
```
*Note*: requires installing CircleCI and docker locally on your machine.
5) Go to [github.com/OpenZeppelin/openzeppelin-contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) in your web browser and issue a new pull request.
*IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions

@ -2,7 +2,6 @@
[![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](https://docs.openzeppelin.com/contracts)
[![NPM Package](https://img.shields.io/npm/v/@openzeppelin/contracts.svg)](https://www.npmjs.org/package/@openzeppelin/contracts)
[![Build Status](https://circleci.com/gh/OpenZeppelin/openzeppelin-contracts.svg?style=shield)](https://circleci.com/gh/OpenZeppelin/openzeppelin-contracts)
[![Coverage Status](https://codecov.io/gh/OpenZeppelin/openzeppelin-contracts/graph/badge.svg)](https://codecov.io/gh/OpenZeppelin/openzeppelin-contracts)
**A library for secure smart contract development.** Build on a solid foundation of community-vetted code.
@ -11,7 +10,7 @@
* Flexible [role-based permissioning](https://docs.openzeppelin.com/contracts/access-control) scheme.
* Reusable [Solidity components](https://docs.openzeppelin.com/contracts/utilities) to build custom contracts and complex decentralized systems.
* First-class integration with the [Gas Station Network](https://docs.openzeppelin.com/contracts/gsn) for systems with no gas fees!
* Audited by leading security firms (_last full audit on v2.0.0_).
* [Audited](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/audit) by leading security firms (_last full audit on v2.0.0_).
## Overview

@ -0,0 +1,20 @@
const fs = require('fs');
const path = require('path');
usePlugin('solidity-coverage');
usePlugin('@nomiclabs/buidler-truffle5');
for (const f of fs.readdirSync(path.join(__dirname, 'buidler'))) {
require(path.join(__dirname, 'buidler', f));
}
module.exports = {
networks: {
buidlerevm: {
blockGasLimit: 10000000,
},
},
solc: {
version: '0.6.12',
},
};

@ -0,0 +1,10 @@
extendEnvironment(env => {
const { contract } = env;
env.contract = function (name, body) {
// remove the default account from the accounts list used in tests, in order
// to protect tests against accidentally passing due to the contract
// deployer being used subsequently as function caller
contract(name, accounts => body(accounts.slice(1)));
};
});

@ -5,8 +5,85 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/
Contract modules for authorization and access control mechanisms.
== Contracts
== Authorization
{{Ownable}}
{{AccessControl}}
== Timelock
{{TimelockController}}
==== Terminology
* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.
* *Operation status:*
** *Unset:* An operation that is not part of the timelock mechanism.
** *Pending:* An operation that has been scheduled, before the timer expires.
** *Ready:* An operation that has been scheduled, after the timer expires.
** *Done:* An operation that has been executed.
* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations.
* *Role*:
** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations.
** *Executor:* An address (smart contract or EOA) that is in charge of executing operations.
==== Operation structure
Operation executed by the xref:api:access.adoc#TimelockController[`TimelockControler`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
Both operations contain:
* *Target*, the address of the smart contract that the timelock should operate on.
* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction.
* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encode using web3js as follows:
```javascript
const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
```
* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency.
* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value.
In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length.
==== Operation lifecycle
Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle:
`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done`
* By calling xref:api:access.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:access.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:access.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method.
* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed.
* By calling xref:api:access.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:access.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed.
* xref:api:access.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled.
Operations status can be queried using the functions:
* xref:api:access.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`]
* xref:api:access.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`]
==== Roles
===== Admin
The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process.
This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5`
===== Proposer
The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO.
WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers.
This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1`
===== Executor
The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executor can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers.
This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63`
WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management.

@ -0,0 +1,281 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "./../math/SafeMath.sol";
import "./AccessControl.sol";
/**
* @dev Contract module which acts as a timelocked controller. When set as the
* owner of an `Ownable` smart contract, it enforces a timelock on all
* `onlyOwner` maintenance operations. This gives time for users of the
* controlled contract to exit before a potentially dangerous maintenance
* operation is applied.
*
* By default, this contract is self administered, meaning administration tasks
* have to go through the timelock process. The proposer (resp executor) role
* is in charge of proposing (resp executing) operations. A common use case is
* to position this {TimelockController} as the owner of a smart contract, with
* a multisig or a DAO as the sole proposer.
*/
contract TimelockController is AccessControl {
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
mapping(bytes32 => uint256) private _timestamps;
uint256 private _minDelay;
/**
* @dev Emitted when a call is scheduled as part of operation `id`.
*/
event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay);
/**
* @dev Emitted when a call is performed as part of operation `id`.
*/
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
/**
* @dev Emitted when operation `id` is cancelled.
*/
event Cancelled(bytes32 indexed id);
/**
* @dev Emitted when the minimum delay for future operations is modified.
*/
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
/**
* @dev Initializes the contract with a given `minDelay`.
*/
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) public {
_setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
// deployer + self administration
_setupRole(TIMELOCK_ADMIN_ROLE, _msgSender());
_setupRole(TIMELOCK_ADMIN_ROLE, address(this));
// register proposers
for (uint256 i = 0; i < proposers.length; ++i) {
_setupRole(PROPOSER_ROLE, proposers[i]);
}
// register executors
for (uint256 i = 0; i < executors.length; ++i) {
_setupRole(EXECUTOR_ROLE, executors[i]);
}
_minDelay = minDelay;
emit MinDelayChange(0, minDelay);
}
/**
* @dev Modifier to make a function callable only by a certain role. In
* addition to checking the sender's role, `address(0)` 's role is also
* considered. Granting a role to `address(0)` is equivalent to enabling
* this role for everyone.
*/
modifier onlyRole(bytes32 role) {
require(hasRole(role, _msgSender()) || hasRole(role, address(0)), "TimelockController: sender requires permission");
_;
}
/*
* @dev Contract might receive/hold ETH as part of the maintenance process.
*/
receive() external payable {}
/**
* @dev Returns whether an operation is pending or not.
*/
function isOperationPending(bytes32 id) public view returns (bool pending) {
return _timestamps[id] > _DONE_TIMESTAMP;
}
/**
* @dev Returns whether an operation is ready or not.
*/
function isOperationReady(bytes32 id) public view returns (bool ready) {
// solhint-disable-next-line not-rely-on-time
return _timestamps[id] > _DONE_TIMESTAMP && _timestamps[id] <= block.timestamp;
}
/**
* @dev Returns whether an operation is done or not.
*/
function isOperationDone(bytes32 id) public view returns (bool done) {
return _timestamps[id] == _DONE_TIMESTAMP;
}
/**
* @dev Returns the timestamp at with an operation becomes ready (0 for
* unset operations, 1 for done operations).
*/
function getTimestamp(bytes32 id) public view returns (uint256 timestamp) {
return _timestamps[id];
}
/**
* @dev Returns the minimum delay for an operation to become valid.
*/
function getMinDelay() public view returns (uint256 duration) {
return _minDelay;
}
/**
* @dev Returns the identifier of an operation containing a single
* transaction.
*/
function hashOperation(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public pure returns (bytes32 hash) {
return keccak256(abi.encode(target, value, data, predecessor, salt));
}
/**
* @dev Returns the identifier of an operation containing a batch of
* transactions.
*/
function hashOperationBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public pure returns (bytes32 hash) {
return keccak256(abi.encode(targets, values, datas, predecessor, salt));
}
/**
* @dev Schedule an operation containing a single transaction.
*
* Emits a {CallScheduled} event.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function schedule(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_schedule(id, delay);
emit CallScheduled(id, 0, target, value, data, predecessor, delay);
}
/**
* @dev Schedule an operation containing a batch of transactions.
*
* Emits one {CallScheduled} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function scheduleBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == datas.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
_schedule(id, delay);
for (uint256 i = 0; i < targets.length; ++i) {
emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay);
}
}
/**
* @dev Schedule an operation that is to becomes valid after a given delay.
*/
function _schedule(bytes32 id, uint256 delay) private {
require(_timestamps[id] == 0, "TimelockController: operation already scheduled");
require(delay >= _minDelay, "TimelockController: insufficient delay");
// solhint-disable-next-line not-rely-on-time
_timestamps[id] = SafeMath.add(block.timestamp, delay);
}
/**
* @dev Cancel an operation.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) {
require(isOperationPending(id), "TimelockController: operation cannot be cancelled");
delete _timestamps[id];
emit Cancelled(id);
}
/**
* @dev Execute an (ready) operation containing a single transaction.
*
* Emits a {CallExecuted} event.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
function execute(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_beforeCall(predecessor);
_call(id, 0, target, value, data);
_afterCall(id);
}
/**
* @dev Execute an (ready) operation containing a batch of transactions.
*
* Emits one {CallExecuted} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == datas.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
_beforeCall(predecessor);
for (uint256 i = 0; i < targets.length; ++i) {
_call(id, i, targets[i], values[i], datas[i]);
}
_afterCall(id);
}
/**
* @dev Checks before execution of an operation's calls.
*/
function _beforeCall(bytes32 predecessor) private view {
require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency");
}
/**
* @dev Checks after execution of an operation's calls.
*/
function _afterCall(bytes32 id) private {
require(isOperationReady(id), "TimelockController: operation is not ready");
_timestamps[id] = _DONE_TIMESTAMP;
}
/**
* @dev Execute an operation's call.
*
* Emits a {CallExecuted} event.
*/
function _call(bytes32 id, uint256 index, address target, uint256 value, bytes calldata data) private {
// solhint-disable-next-line avoid-low-level-calls
(bool success,) = target.call{value: value}(data);
require(success, "TimelockController: underlying transaction reverted");
emit CallExecuted(id, index, target, value, data);
}
/**
* @dev Changes the timelock duration for future operations.
*
* Emits a {MinDelayChange} event.
*/
function updateDelay(uint256 newDelay) external virtual {
require(msg.sender == address(this), "TimelockController: caller must be timelock");
emit MinDelayChange(_minDelay, newDelay);
_minDelay = newDelay;
}
}

@ -52,13 +52,8 @@ library ECDSA {
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("ECDSA: invalid signature 's' value");
}
if (v != 27 && v != 28) {
revert("ECDSA: invalid signature 'v' value");
}
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);

@ -5,6 +5,8 @@ pragma solidity ^0.7.0;
import "../utils/Address.sol";
contract AddressImpl {
string public sharedAnswer;
event CallReturnValue(string data);
function isContract(address account) external view returns (bool) {
@ -17,13 +19,21 @@ contract AddressImpl {
function functionCall(address target, bytes calldata data) external {
bytes memory returnData = Address.functionCall(target, data);
emit CallReturnValue(abi.decode(returnData, (string)));
}
function functionCallWithValue(address target, bytes calldata data, uint256 value) external payable {
bytes memory returnData = Address.functionCallWithValue(target, data, value);
emit CallReturnValue(abi.decode(returnData, (string)));
}
function functionStaticCall(address target, bytes calldata data) external {
bytes memory returnData = Address.functionStaticCall(target, data);
emit CallReturnValue(abi.decode(returnData, (string)));
}
function functionDelegateCall(address target, bytes calldata data) external {
bytes memory returnData = Address.functionDelegateCall(target, data);
emit CallReturnValue(abi.decode(returnData, (string)));
}

@ -3,6 +3,7 @@
pragma solidity ^0.7.0;
contract CallReceiverMock {
string public sharedAnswer;
event MockFunctionCalled();
@ -20,6 +21,10 @@ contract CallReceiverMock {
return "0x1234";
}
function mockStaticFunction() public pure returns (string memory) {
return "0x1234";
}
function mockFunctionRevertsNoReason() public payable {
revert();
}
@ -37,4 +42,9 @@ contract CallReceiverMock {
_array.push(i);
}
}
function mockFunctionWritesStorage() public returns (string memory) {
sharedAnswer = "42";
return "0x1234";
}
}

@ -23,5 +23,5 @@ contract Create2Impl {
return Create2.computeAddress(salt, codeHash, deployer);
}
receive() payable external {}
receive() external payable {}
}

@ -15,7 +15,7 @@ contract DummyImplementation {
value = 10;
}
function initializePayable() payable public {
function initializePayable() public payable {
value = 100;
}
@ -23,7 +23,7 @@ contract DummyImplementation {
value = _value;
}
function initializePayable(uint256 _value) payable public {
function initializePayable(uint256 _value) public payable {
value = _value;
}
@ -47,7 +47,7 @@ contract DummyImplementation {
}
contract DummyImplementationV2 is DummyImplementation {
function migrate(uint256 newVal) payable public {
function migrate(uint256 newVal) public payable {
value = newVal;
}

@ -4,6 +4,37 @@ pragma solidity ^0.7.0;
import "../utils/EnumerableSet.sol";
// Bytes32Set
contract EnumerableBytes32SetMock {
using EnumerableSet for EnumerableSet.Bytes32Set;
event OperationResult(bool result);
EnumerableSet.Bytes32Set private _set;
function contains(bytes32 value) public view returns (bool) {
return _set.contains(value);
}
function add(bytes32 value) public {
bool result = _set.add(value);
emit OperationResult(result);
}
function remove(bytes32 value) public {
bool result = _set.remove(value);
emit OperationResult(result);
}
function length() public view returns (uint256) {
return _set.length();
}
function at(uint256 index) public view returns (bytes32) {
return _set.at(index);
}
}
// AddressSet
contract EnumerableAddressSetMock {
using EnumerableSet for EnumerableSet.AddressSet;
@ -64,4 +95,4 @@ contract EnumerableUintSetMock {
function at(uint256 index) public view returns (uint256) {
return _set.at(index);
}
}
}

@ -1,7 +1,7 @@
{
"name": "@openzeppelin/contracts",
"version": "3.2.2-solc-0.7",
"description": "Secure Smart Contract library for Solidity",
"version": "3.2.2-solc-0.7",
"files": [
"**/*.sol",
"/build/contracts/*.json",

@ -60,7 +60,7 @@ abstract contract Proxy {
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback () payable external {
fallback () external payable {
_fallback();
}
@ -68,7 +68,7 @@ abstract contract Proxy {
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive () payable external {
receive () external payable {
_fallback();
}

@ -23,7 +23,7 @@ import "./UpgradeableProxy.sol";
* to sudden errors when trying to call a function from the proxy implementation.
*
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
* you should think of the `ProxyAdmin` instance as the real administrative inerface of your proxy.
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*/
contract TransparentUpgradeableProxy is UpgradeableProxy {
/**

@ -5,7 +5,6 @@ pragma solidity ^0.7.0;
import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @dev Implementation of the {IERC20} interface.
@ -33,7 +32,6 @@ import "../../utils/Address.sol";
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances;
@ -142,9 +140,10 @@ contract ERC20 is Context, IERC20 {
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
@ -222,7 +221,7 @@ contract ERC20 is Context, IERC20 {
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
* Requirements:
*
* - `to` cannot be the zero address.
*/
@ -242,7 +241,7 @@ contract ERC20 is Context, IERC20 {
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.

@ -17,6 +17,5 @@ interface IERC721Receiver {
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external returns (bytes4);
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

@ -24,14 +24,14 @@ library Address {
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
assembly { size := extcodesize(account) }
return size > 0;
}
/**
@ -87,7 +87,7 @@ library Address {
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
@ -113,14 +113,62 @@ library Address {
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.3._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.3._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {

@ -23,8 +23,8 @@ pragma solidity ^0.7.0;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
@ -132,6 +132,60 @@ library EnumerableSet {
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {

@ -1,4 +1,8 @@
* xref:index.adoc[Overview]
* xref:extending-contracts.adoc[Extending Contracts]
* xref:upgradeable.adoc[Using with Upgrades]
* xref:releases-stability.adoc[Releases & Stability]
* xref:access-control.adoc[Access Control]
@ -12,8 +16,4 @@
* xref:gsn.adoc[Gas Station Network]
** xref:gsn-strategies.adoc[Strategies]
* xref:extending-contracts.adoc[Extending Contracts]
* xref:utilities.adoc[Utilities]
* xref:releases-stability.adoc[Releases & Stability]

@ -185,3 +185,37 @@ for (let i = 0; i < minterCount; ++i) {
members.push(await myToken.getRoleMember(MINTER_ROLE, i));
}
```
== Delayed operation
Access control is essential to prevent unauthorized access to critical functions. These functions may be used to mint tokens, freeze transfers or perform an upgrade that completely changes the smart contract logic. While xref:api:access.adoc#Ownable[`Ownable`] and xref:api:access.adoc#AccessControl[`AccessControl`] can prevent unauthorized access, they do not address the issue of a misbehaving administrator attacking their own system to the prejudice of their users.
This is the issue the xref:api:access.adoc#TimelockController[`TimelockControler`] is addressing.
The xref:api:access.adoc#TimelockController[`TimelockControler`] is a proxy that is governed by proposers and executors. When set as the owner/admin/controller of a smart contract, it ensures that whichever maintenance operation is ordered by the proposers is subject to a delay. This delay protects the users of the smart contract by giving them time to review the maintenance operation and exit the system if they consider it is in their best interest to do so.
=== Using `TimelockControler`
By default, the address that deployed the xref:api:access.adoc#TimelockController[`TimelockControler`] gets administration privileges over the timelock. This role grants the right to assign proposers, executors, and other administrators.
The first step in configuring the xref:api:access.adoc#TimelockController[`TimelockControler`] is to assign at least one proposer and one executor. These can be assigned during construction or later by anyone with the administrator role. These roles are not exclusive, meaning an account can have both roles.
Roles are managed using the xref:api:access.adoc#AccessControl[`AccessControl`] interface and the `bytes32` values for each role are accessible through the `ADMIN_ROLE`, `PROPOSER_ROLE` and `EXECUTOR_ROLE` constants.
There is an additional feature built on top of `AccessControl`: giving the proposer or executor role to `address(0)` opens access to anyone. This feature, while potentially useful for testing and in some cases for the executor role, is dangerous and should be used with caution.
At this point, with both a proposer and an executor assigned, the timelock can perform operations.
An optional next step is for the deployer to renounce its administrative privileges and leave the timelock self-administered. If the deployer decides to do so, all further maintenance, including assigning new proposers/schedulers or changing the timelock duration will have to follow the timelock workflow. This links the governance of the timelock to the governance of contracts attached to the timelock, and enforce a delay on timelock maintenance operations.
WARNING: If the deployer renounces administrative rights in favour of timelock itself, assigning new proposers or executors will require a timelocked operation. This means that if the accounts in charge of any of these two roles become unavailable, then the entire contract (and any contract it controls) becomes locked indefinitely.
With both the proposer and executor roles assigned and the timelock in charge of its own administration, you can now transfer the ownership/control of any contract to the timelock.
TIP: A recommended configuration is to grant both roles to a secure governance contract such as a DAO or a multisig, and to additionally grant the executor role to a few EOAs held by people in charge of helping with the maintenance operations. These wallets cannot take over control of the timelock but they can help smoothen the workflow.
=== Minimum delay
Operations executed by the xref:api:access.adoc#TimelockController[`TimelockControler`] are not subject to a fixed delay but rather a minimum delay. Some major updates might call for a longer delay. For example, if a delay of just a few days might be sufficient for users to audit a minting operation, it makes sense to use a delay of a few weeks, or even a few months, when scheduling a smart contract upgrade.
The minimum delay (accessible through the xref:api:access.adoc#TimelockController-getMinDelay--[`getMinDelay`] method) can be updated by calling the xref:api:access.adoc#TimelockController-updateDelay-uint256-[`updateDelay`] function. Bear in mind that access to this function is only accessible by the timelock itself, meaning this maintenance operation has to go through the timelock itself.

@ -45,7 +45,7 @@ contract GameItems is ERC1155 {
uint256 public constant SWORD = 3;
uint256 public constant SHIELD = 4;
constructor() ERC1155("https://game.example/api/item/{1}.json") {
constructor() ERC1155("https://game.example/api/item/{id}.json") {
_mint(msg.sender, GOLD, 10**18, "");
_mint(msg.sender, SILVER, 10**27, "");
_mint(msg.sender, THORS_HAMMER, 1, "");

@ -6,7 +6,7 @@
* Flexible xref:access-control.adoc[role-based permissioning] scheme.
* Reusable xref:utilities.adoc[Solidity components] to build custom contracts and complex decentralized systems.
* First-class integration with the xref:gsn.adoc[Gas Station Network] for systems with no gas fees!
* Audited by leading security firms (_last full audit on v2.0.0_).
* https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/audit[Audited] by leading security firms (_last full audit on v2.0.0_).
== Overview

@ -0,0 +1,71 @@
= Using with Upgrades
If your contract is going to be deployed with upgradeability, such as using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins], you will need to use the Upgrade Safe variant of OpenZeppelin Contracts.
This variant is available as a separate package called `@openzeppelin/contracts-upgradeable`, which is hosted in the repository https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[OpenZeppelin/openzeppelin-contracts-upgradeable].
It follows all of the rules for xref:upgrades-plugins::writing-upgradeable.adoc[Writing Upgradeable Contracts]: constructors are replaced by initializer functions, state variables are initialized in initializer functions, and we additionally check for storage incompatibilities across minor versions.
== Overview
=== Installation
```console
$ npm install @openzeppelin/contracts-upgradeable
```
=== Usage
The package replicates the structure of the main OpenZeppelin Contracts package, but every file and contract has the suffix `Upgradeable`.
```diff
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
-contract MyCollectible is ERC721 {
+contract MyCollectible is ERC721Upgradeable {
```
Constructors are replaced by internal initializer functions following the naming convention `+__{ContractName}_init+`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend.
```diff
- constructor() ERC721("MyCollectible", "MCO") public {
+ function initialize() initializer public {
+ __ERC721_init("MyCollectible", "MCO");
}
```
CAUTION: Use with multiple inheritance requires special attention. See the section below titled <<multiple-inheritance>>.
Once this contract is set up and compiled, you can deploy it using the xref:upgrades-plugins::index.adoc[Upgrades Plugins]. The following snippet shows an example deployment script using Hardhat.
```js
// scripts/deploy-my-collectible.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const MyCollectible = await ethers.getContractFactory("MyCollectible");
const mc = await upgrades.deployProxy(MyCollectible);
await mc.deployed();
console.log("MyCollectible deployed to:", mc.address);
}
main();
```
== Further Notes
[[multiple-inheritance]]
=== Multiple Inheritance
Initializer functions are not linearized by the compiler like constructors. Because of this, each `+__{ContractName}_init+` function embeds the linearized calls to all parent initializers. As a consequence, calling two of these `init` functions can potentially initialize the same contract twice.
The function `+__{ContractName}_init_unchained+` found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins.
=== Storage Gaps
You may notice that every contract includes a state variable named `+__gap+`. This is empty reserved space in storage that is put in place in Upgrade Safe contracts. It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments.
It isn't safe to simply add a state variable because it "shifts down" all of the state variables below in the inheritance chain. This makes the storage layouts incompatible, as explained in xref:upgrades-plugins::writing-upgradeable.adoc#modifying-your-contracts[Writing Upgradeable Contracts]. The size of the `+__gap+` array is calculated so that the amount of storage used by a contract always adds up to the same number (in this case 50 storage slots).

@ -1,17 +1,16 @@
{
"name": "openzeppelin-solidity",
"version": "3.2.2-solc-0.7",
"description": "Secure Smart Contract library for Solidity",
"version": "3.2.2-solc-0.7",
"files": [
"/contracts/**/*.sol",
"/build/contracts/*.json",
"!/contracts/mocks",
"!/contracts/examples",
"/test/behaviors"
],
"scripts": {
"compile": "scripts/compile.sh",
"coverage": "scripts/coverage.sh",
"compile": "buidler compile",
"coverage": "buidler coverage",
"docs": "oz-docs",
"docs:watch": "npm run docs watch contracts 'docs/*.hbs'",
"prepare-docs": "scripts/prepare-docs.sh",
@ -20,12 +19,12 @@
"lint:js": "eslint --ignore-path .gitignore .",
"lint:js:fix": "eslint --ignore-path .gitignore . --fix",
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
"prepublish": "rimraf build contracts/build",
"prepublish": "rimraf build contracts/build artifacts cache",
"prepare": "npm run compile",
"prepack": "node scripts/remove-ignored-artifacts.js",
"prepack": "scripts/prepack.sh",
"release": "scripts/release/release.sh",
"version": "scripts/release/version.sh",
"test": "mocha --exit --recursive"
"test": "buidler test"
},
"repository": {
"type": "git",
@ -46,12 +45,13 @@
},
"homepage": "https://openzeppelin.com/contracts/",
"devDependencies": {
"@openzeppelin/cli": "^2.8.2",
"@nomiclabs/buidler": "^1.4.8",
"@nomiclabs/buidler-truffle5": "^1.3.4",
"@nomiclabs/buidler-web3": "^1.3.4",
"@openzeppelin/docs-utils": "^0.1.0",
"@openzeppelin/gsn-helpers": "^0.2.3",
"@openzeppelin/gsn-provider": "^0.1.10",
"@openzeppelin/test-environment": "^0.1.4",
"@openzeppelin/test-helpers": "^0.5.6",
"@openzeppelin/test-helpers": "^0.5.9",
"chai": "^4.2.0",
"eslint": "^6.5.1",
"eslint-config-standard": "^14.1.1",
@ -60,15 +60,14 @@
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"ethereumjs-util": "^7.0.1",
"ganache-core-coverage": "https://github.com/OpenZeppelin/ganache-core-coverage/releases/download/2.5.3-coverage/ganache-core-coverage-2.5.3.tgz",
"ethereumjs-util": "^7.0.7",
"lodash.startcase": "^4.4.0",
"lodash.zip": "^4.2.0",
"micromatch": "^4.0.2",
"mocha": "^8.0.1",
"rimraf": "^3.0.2",
"solhint": "^3.2.0",
"solidity-coverage": "github:rotcivegaf/solidity-coverage#5875f5b7bc74d447f3312c9c0e9fc7814b482477",
"solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3"
},
"dependencies": {}

@ -1,9 +1,6 @@
{
"rangeStrategy": "update-lockfile",
"extends": [
"config:base",
"group:allNonMajor",
"schedule:weekly"
"github>OpenZeppelin/code-style"
],
"packageRules": [
{

@ -1,9 +0,0 @@
#!/usr/bin/env sh
if [ "$SOLC_NIGHTLY" = true ]; then
docker pull ethereum/solc:nightly
fi
export OPENZEPPELIN_NON_INTERACTIVE=true
npx oz compile

@ -1,25 +0,0 @@
#!/usr/bin/env bash
set -o errexit -o pipefail
# Executes cleanup function at script exit.
trap cleanup EXIT
cleanup() {
# Delete the symlink created to the allFiredEvents file solidity-coverage creates
rm -f allFiredEvents
}
log() {
echo "$*" >&2
}
# The allFiredEvents file is created inside coverageEnv, but solidity-coverage
# expects it to be at the top level. We create a symlink to fix this
ln -s coverageEnv/allFiredEvents allFiredEvents
OZ_TEST_ENV_COVERAGE=true npx solidity-coverage || log "Test run failed"
if [ "$CI" = true ]; then
curl -s https://codecov.io/bash | bash -s -- -C "$CIRCLE_SHA1"
fi

@ -7,7 +7,7 @@ const startCase = require('lodash.startcase');
const baseDir = process.argv[2];
const files = proc.execFileSync(
'find', [baseDir, '-type', 'f'], { encoding: 'utf8' }
'find', [baseDir, '-type', 'f'], { encoding: 'utf8' },
).split('\n').filter(s => s !== '');
console.log('.API');

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
# cross platform `mkdir -p`
node -e 'fs.mkdirSync("build/contracts", { recursive: true })'
cp artifacts/*.json build/contracts
node scripts/remove-ignored-artifacts.js

@ -0,0 +1,11 @@
const path = require('path');
const bre = require('@nomiclabs/buidler');
const { Compiler } = require('@nomiclabs/buidler/internal/solidity/compiler');
const compiler = new Compiler(
bre.config.solc.version,
path.join(bre.config.paths.cache, 'compilers'),
);
module.exports = Object.assign(compiler.getSolc(), { __esModule: true });

@ -9,5 +9,12 @@ if [ ! -d node_modules ]; then
fi
rm -rf "$OUTDIR"
solidity-docgen -t docs -o "$OUTDIR" -e contracts/mocks,contracts/examples --output-structure readmes
solidity-docgen \
-t docs \
-o "$OUTDIR" \
-e contracts/mocks,contracts/examples \
--output-structure readmes \
--solc-module scripts/prepare-docs-solc.js
node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"

@ -24,7 +24,7 @@ const { version } = require('../../package.json');
fs.writeFileSync('CHANGELOG.md', changelog.replace(
unreleased,
`## ${version} (${new Date().toISOString().split('T')[0]})`)
`## ${version} (${new Date().toISOString().split('T')[0]})`),
);
cp.execSync('git add CHANGELOG.md', { stdio: 'inherit' });

@ -23,20 +23,19 @@ const ignorePatternsSubtrees = ignorePatterns
.concat(ignorePatterns.map(pat => path.join(pat, '**/*')))
.map(p => p.replace(/^\//, ''));
const solcOutput = readJSON('cache/solc-output.json');
const artifactsDir = 'build/contracts';
let n = 0;
for (const artifact of fs.readdirSync(artifactsDir)) {
const fullArtifactPath = path.join(artifactsDir, artifact);
const { sourcePath: fullSourcePath } = readJSON(fullArtifactPath);
const sourcePath = path.relative('.', fullSourcePath);
for (const sourcePath in solcOutput.contracts) {
const ignore = match.any(sourcePath, ignorePatternsSubtrees);
if (ignore) {
fs.unlinkSync(fullArtifactPath);
n += 1;
for (const contract in solcOutput.contracts[sourcePath]) {
fs.unlinkSync(path.join(artifactsDir, contract + '.json'));
n += 1;
}
}
}

@ -1,22 +0,0 @@
const { GSNDevProvider } = require('@openzeppelin/gsn-provider');
module.exports = {
accounts: {
ether: 1e6,
},
contracts: {
type: 'truffle',
},
setupProvider: (baseProvider) => {
const { accounts } = require('@openzeppelin/test-environment');
return new GSNDevProvider(baseProvider, {
txfee: 70,
useGSN: false,
ownerAddress: accounts[8],
relayerAddress: accounts[9],
});
},
};

@ -1,8 +1,6 @@
const { contract } = require('@openzeppelin/test-environment');
const { BN, expectEvent } = require('@openzeppelin/test-helpers');
const ContextMock = contract.fromArtifact('ContextMock');
const ContextMock = artifacts.require('ContextMock');
function shouldBehaveLikeRegularContext (sender) {
describe('msgSender', function () {

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers');
const ContextMock = contract.fromArtifact('ContextMock');
const ContextMockCaller = contract.fromArtifact('ContextMockCaller');
const ContextMock = artifacts.require('ContextMock');
const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('./Context.behavior');
describe('Context', function () {
contract('Context', function (accounts) {
const [ sender ] = accounts;
beforeEach(async function () {

@ -1,20 +1,23 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { constants, expectEvent } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3');
const ERC721GSNRecipientMock = contract.fromArtifact('ERC721GSNRecipientMock');
const ERC721GSNRecipientMock = artifacts.require('ERC721GSNRecipientMock');
describe('ERC721GSNRecipient (integration)', function () {
contract('ERC721GSNRecipient (integration)', function (accounts) {
const [ signer, sender ] = accounts;
const name = 'Non Fungible Token';
const symbol = 'NFT';
const tokenId = '42';
before(function () {
setGSNProvider(ERC721GSNRecipientMock, accounts);
});
beforeEach(async function () {
this.token = await ERC721GSNRecipientMock.new(name, symbol, signer);
});
@ -41,9 +44,9 @@ describe('ERC721GSNRecipient (integration)', function () {
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, this.token.address
), signer
)
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, this.token.address,
), signer,
),
);
await testMintToken(this.token, sender, tokenId, { useGSN: true, approveFunction });

@ -1,20 +1,24 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { balance, BN, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai');
const GSNRecipientMock = contract.fromArtifact('GSNRecipientMock');
const ContextMockCaller = contract.fromArtifact('ContextMockCaller');
const GSNRecipientMock = artifacts.require('GSNRecipientMock');
const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('./Context.behavior');
describe('GSNRecipient', function () {
contract('GSNRecipient', function (accounts) {
const [ payee, sender, newRelayHub ] = accounts;
before(function () {
setGSNProvider(GSNRecipientMock, accounts);
setGSNProvider(ContextMockCaller, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientMock.new();
});
@ -38,13 +42,13 @@ describe('GSNRecipient', function () {
it('cannot upgrade to the same RelayHub', async function () {
await expectRevert(
this.recipient.upgradeRelayHub(singletonRelayHub),
'GSNRecipient: new RelayHub is the current one'
'GSNRecipient: new RelayHub is the current one',
);
});
it('cannot upgrade to the zero address', async function () {
await expectRevert(
this.recipient.upgradeRelayHub(ZERO_ADDRESS), 'GSNRecipient: new RelayHub is the zero address'
this.recipient.upgradeRelayHub(ZERO_ADDRESS), 'GSNRecipient: new RelayHub is the zero address',
);
});

@ -1,20 +1,25 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { ether, expectEvent } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai');
const GSNRecipientERC20FeeMock = contract.fromArtifact('GSNRecipientERC20FeeMock');
const ERC20 = contract.fromArtifact('ERC20');
const IRelayHub = contract.fromArtifact('IRelayHub');
const GSNRecipientERC20FeeMock = artifacts.require('GSNRecipientERC20FeeMock');
const ERC20 = artifacts.require('ERC20');
const IRelayHub = artifacts.require('IRelayHub');
describe('GSNRecipientERC20Fee', function () {
contract('GSNRecipientERC20Fee', function (accounts) {
const [ sender ] = accounts;
const name = 'FeeToken';
const symbol = 'FTKN';
before(function () {
setGSNProvider(GSNRecipientERC20FeeMock, accounts);
setGSNProvider(ERC20, accounts);
setGSNProvider(IRelayHub, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol);
this.token = await ERC20.at(await this.recipient.token());

@ -1,16 +1,19 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectEvent, expectRevert, constants } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3');
const { ZERO_ADDRESS } = constants;
const GSNRecipientSignatureMock = contract.fromArtifact('GSNRecipientSignatureMock');
const GSNRecipientSignatureMock = artifacts.require('GSNRecipientSignatureMock');
describe('GSNRecipientSignature', function () {
contract('GSNRecipientSignature', function (accounts) {
const [ signer, other ] = accounts;
before(function () {
setGSNProvider(GSNRecipientSignatureMock, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientSignatureMock.new(signer);
});
@ -26,9 +29,9 @@ describe('GSNRecipientSignature', function () {
it('fails when constructor called with a zero address', async function () {
await expectRevert(
GSNRecipientSignatureMock.new(
ZERO_ADDRESS
ZERO_ADDRESS,
),
'GSNRecipientSignature: trusted signer is the zero address'
'GSNRecipientSignature: trusted signer is the zero address',
);
});
});
@ -49,9 +52,9 @@ describe('GSNRecipientSignature', function () {
web3.utils.soliditySha3(
// the nonce is not signed
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas)
), signer
)
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas),
), signer,
),
);
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));
@ -63,9 +66,9 @@ describe('GSNRecipientSignature', function () {
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to
), signer
)
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), signer,
),
);
const { tx } = await this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction });
@ -79,9 +82,9 @@ describe('GSNRecipientSignature', function () {
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to
), other
)
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), other,
),
);
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));

@ -1,3 +1,3 @@
## Testing
Unit test are critical to the OpenZeppelin framework. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory. OpenZeppelin uses Truffle testing framework(based on the Mocha JavaScript testing framework) and the Chai assertion library. To learn more about how tests are structured, please reference OpenZeppelin’s Testing Guide.
Unit test are critical to the OpenZeppelin framework. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory.

@ -1,12 +1,10 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const AccessControlMock = contract.fromArtifact('AccessControlMock');
const AccessControlMock = artifacts.require('AccessControlMock');
describe('AccessControl', function () {
contract('AccessControl', function (accounts) {
const [ admin, authorized, otherAuthorized, other, otherAdmin ] = accounts;
const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
@ -42,7 +40,7 @@ describe('AccessControl', function () {
it('non-admin cannot grant role to other accounts', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: other }),
'AccessControl: sender must be an admin to grant'
'AccessControl: sender must be an admin to grant',
);
});
@ -76,7 +74,7 @@ describe('AccessControl', function () {
it('non-admin cannot revoke role', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: other }),
'AccessControl: sender must be an admin to revoke'
'AccessControl: sender must be an admin to revoke',
);
});
@ -110,7 +108,7 @@ describe('AccessControl', function () {
it('only the sender can renounce their roles', async function () {
await expectRevert(
this.accessControl.renounceRole(ROLE, authorized, { from: admin }),
'AccessControl: can only renounce roles for self'
'AccessControl: can only renounce roles for self',
);
});
@ -170,14 +168,14 @@ describe('AccessControl', function () {
it('a role\'s previous admins no longer grant roles', async function () {
await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to grant'
'AccessControl: sender must be an admin to grant',
);
});
it('a role\'s previous admins no longer revoke roles', async function () {
await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to revoke'
'AccessControl: sender must be an admin to revoke',
);
});
});

@ -1,12 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const Ownable = contract.fromArtifact('OwnableMock');
const Ownable = artifacts.require('OwnableMock');
describe('Ownable', function () {
contract('Ownable', function (accounts) {
const [ owner, other ] = accounts;
beforeEach(async function () {
@ -28,14 +27,14 @@ describe('Ownable', function () {
it('prevents non-owners from transferring', async function () {
await expectRevert(
this.ownable.transferOwnership(other, { from: other }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
});
it('guards ownership against stuck state', async function () {
await expectRevert(
this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }),
'Ownable: new owner is the zero address'
'Ownable: new owner is the zero address',
);
});
});
@ -51,7 +50,7 @@ describe('Ownable', function () {
it('prevents non-owners from renouncement', async function () {
await expectRevert(
this.ownable.renounceOwnership({ from: other }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
});
});

@ -0,0 +1,992 @@
const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers');
const { ZERO_BYTES32 } = constants;
const { expect } = require('chai');
const TimelockController = artifacts.require('TimelockController');
const CallReceiverMock = artifacts.require('CallReceiverMock');
const Implementation2 = artifacts.require('Implementation2');
const MINDELAY = time.duration.days(1);
function genOperation (target, value, data, predecessor, salt) {
const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([
'address',
'uint256',
'bytes',
'uint256',
'bytes32',
], [
target,
value,
data,
predecessor,
salt,
]));
return { id, target, value, data, predecessor, salt };
}
function genOperationBatch (targets, values, datas, predecessor, salt) {
const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([
'address[]',
'uint256[]',
'bytes[]',
'uint256',
'bytes32',
], [
targets,
values,
datas,
predecessor,
salt,
]));
return { id, targets, values, datas, predecessor, salt };
}
contract('TimelockController', function (accounts) {
const [ admin, proposer, executor, other ] = accounts;
beforeEach(async function () {
// Deploy new timelock
this.timelock = await TimelockController.new(
MINDELAY,
[ proposer ],
[ executor ],
{ from: admin },
);
// Mocks
this.callreceivermock = await CallReceiverMock.new({ from: admin });
this.implementation2 = await Implementation2.new({ from: admin });
});
it('initial state', async function () {
expect(await this.timelock.getMinDelay()).to.be.bignumber.equal(MINDELAY);
});
describe('methods', function () {
describe('operation hashing', function () {
it('hashOperation', async function () {
this.operation = genOperation(
'0x29cebefe301c6ce1bb36b58654fea275e1cacc83',
'0xf94fdd6e21da21d2',
'0xa3bc5104',
'0xba41db3be0a9929145cfe480bd0f1f003689104d275ae912099f925df424ef94',
'0x60d9109846ab510ed75c15f979ae366a8a2ace11d34ba9788c13ac296db50e6e',
);
expect(await this.timelock.hashOperation(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
)).to.be.equal(this.operation.id);
});
it('hashOperationBatch', async function () {
this.operation = genOperationBatch(
Array(8).fill('0x2d5f21620e56531c1d59c2df9b8e95d129571f71'),
Array(8).fill('0x2b993cfce932ccee'),
Array(8).fill('0xcf51966b'),
'0xce8f45069cc71d25f71ba05062de1a3974f9849b004de64a70998bca9d29c2e7',
'0x8952d74c110f72bfe5accdf828c74d53a7dfb71235dfa8a1e8c75d8576b372ff',
);
expect(await this.timelock.hashOperationBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
)).to.be.equal(this.operation.id);
});
});
describe('simple', function () {
describe('schedule', function () {
beforeEach(async function () {
this.operation = genOperation(
'0x31754f590B97fD975Eb86938f18Cc304E264D2F2',
0,
'0x3bf92ccc',
ZERO_BYTES32,
'0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726',
);
});
it('proposer can schedule', async function () {
const receipt = await this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
);
expectEvent(receipt, 'CallScheduled', {
id: this.operation.id,
index: web3.utils.toBN(0),
target: this.operation.target,
value: web3.utils.toBN(this.operation.value),
data: this.operation.data,
predecessor: this.operation.predecessor,
delay: MINDELAY,
});
const block = await web3.eth.getBlock(receipt.receipt.blockHash);
expect(await this.timelock.getTimestamp(this.operation.id))
.to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY));
});
it('prevent overwritting active operation', async function () {
await this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
);
await expectRevert(
this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
),
'TimelockController: operation already scheduled',
);
});
it('prevent non-proposer from commiting', async function () {
await expectRevert(
this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: other },
),
'TimelockController: sender requires permission',
);
});
it('enforce minimum delay', async function () {
await expectRevert(
this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY - 1,
{ from: proposer },
),
'TimelockController: insufficient delay',
);
});
});
describe('execute', function () {
beforeEach(async function () {
this.operation = genOperation(
'0xAe22104DCD970750610E6FE15E623468A98b15f7',
0,
'0x13e414de',
ZERO_BYTES32,
'0xc1059ed2dc130227aa1d1d539ac94c641306905c020436c636e19e3fab56fc7f',
);
});
it('revert if operation is not scheduled', async function () {
await expectRevert(
this.timelock.execute(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
describe('with scheduled operation', function () {
beforeEach(async function () {
({ receipt: this.receipt, logs: this.logs } = await this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
));
});
it('revert if execution comes too early 1/2', async function () {
await expectRevert(
this.timelock.execute(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
it('revert if execution comes too early 2/2', async function () {
const timestamp = await this.timelock.getTimestamp(this.operation.id);
await time.increaseTo(timestamp - 5); // -1 is too tight, test sometime fails
await expectRevert(
this.timelock.execute(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
describe('on time', function () {
beforeEach(async function () {
const timestamp = await this.timelock.getTimestamp(this.operation.id);
await time.increaseTo(timestamp);
});
it('executor can reveal', async function () {
const receipt = await this.timelock.execute(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
);
expectEvent(receipt, 'CallExecuted', {
id: this.operation.id,
index: web3.utils.toBN(0),
target: this.operation.target,
value: web3.utils.toBN(this.operation.value),
data: this.operation.data,
});
});
it('prevent non-executor from revealing', async function () {
await expectRevert(
this.timelock.execute(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
{ from: other },
),
'TimelockController: sender requires permission',
);
});
});
});
});
});
describe('batch', function () {
describe('schedule', function () {
beforeEach(async function () {
this.operation = genOperationBatch(
Array(8).fill('0xEd912250835c812D4516BBD80BdaEA1bB63a293C'),
Array(8).fill(0),
Array(8).fill('0x2fcb7a88'),
ZERO_BYTES32,
'0x6cf9d042ade5de78bed9ffd075eb4b2a4f6b1736932c2dc8af517d6e066f51f5',
);
});
it('proposer can schedule', async function () {
const receipt = await this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
);
for (const i in this.operation.targets) {
expectEvent(receipt, 'CallScheduled', {
id: this.operation.id,
index: web3.utils.toBN(i),
target: this.operation.targets[i],
value: web3.utils.toBN(this.operation.values[i]),
data: this.operation.datas[i],
predecessor: this.operation.predecessor,
delay: MINDELAY,
});
}
const block = await web3.eth.getBlock(receipt.receipt.blockHash);
expect(await this.timelock.getTimestamp(this.operation.id))
.to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY));
});
it('prevent overwritting active operation', async function () {
await this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
);
await expectRevert(
this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
),
'TimelockController: operation already scheduled',
);
});
it('prevent non-proposer from commiting', async function () {
await expectRevert(
this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: other },
),
'TimelockController: sender requires permission',
);
});
it('enforce minimum delay', async function () {
await expectRevert(
this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY - 1,
{ from: proposer },
),
'TimelockController: insufficient delay',
);
});
});
describe('execute', function () {
beforeEach(async function () {
this.operation = genOperationBatch(
Array(8).fill('0x76E53CcEb05131Ef5248553bEBDb8F70536830b1'),
Array(8).fill(0),
Array(8).fill('0x58a60f63'),
ZERO_BYTES32,
'0x9545eeabc7a7586689191f78a5532443698538e54211b5bd4d7dc0fc0102b5c7',
);
});
it('revert if operation is not scheduled', async function () {
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
describe('with scheduled operation', function () {
beforeEach(async function () {
({ receipt: this.receipt, logs: this.logs } = await this.timelock.scheduleBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
));
});
it('revert if execution comes too early 1/2', async function () {
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
it('revert if execution comes too early 2/2', async function () {
const timestamp = await this.timelock.getTimestamp(this.operation.id);
await time.increaseTo(timestamp - 5); // -1 is to tight, test sometime fails
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: operation is not ready',
);
});
describe('on time', function () {
beforeEach(async function () {
const timestamp = await this.timelock.getTimestamp(this.operation.id);
await time.increaseTo(timestamp);
});
it('executor can reveal', async function () {
const receipt = await this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
);
for (const i in this.operation.targets) {
expectEvent(receipt, 'CallExecuted', {
id: this.operation.id,
index: web3.utils.toBN(i),
target: this.operation.targets[i],
value: web3.utils.toBN(this.operation.values[i]),
data: this.operation.datas[i],
});
}
});
it('prevent non-executor from revealing', async function () {
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: other },
),
'TimelockController: sender requires permission',
);
});
it('length mismatch #1', async function () {
await expectRevert(
this.timelock.executeBatch(
[],
this.operation.values,
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: length mismatch',
);
});
it('length mismatch #2', async function () {
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
[],
this.operation.datas,
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: length mismatch',
);
});
it('length mismatch #3', async function () {
await expectRevert(
this.timelock.executeBatch(
this.operation.targets,
this.operation.values,
[],
this.operation.predecessor,
this.operation.salt,
{ from: executor },
),
'TimelockController: length mismatch',
);
});
});
});
it('partial execution', async function () {
const operation = genOperationBatch(
[
this.callreceivermock.address,
this.callreceivermock.address,
this.callreceivermock.address,
],
[
0,
0,
0,
],
[
this.callreceivermock.contract.methods.mockFunction().encodeABI(),
this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(),
this.callreceivermock.contract.methods.mockFunction().encodeABI(),
],
ZERO_BYTES32,
'0x8ac04aa0d6d66b8812fb41d39638d37af0a9ab11da507afd65c509f8ed079d3e',
);
await this.timelock.scheduleBatch(
operation.targets,
operation.values,
operation.datas,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
await expectRevert(
this.timelock.executeBatch(
operation.targets,
operation.values,
operation.datas,
operation.predecessor,
operation.salt,
{ from: executor },
),
'TimelockController: underlying transaction reverted',
);
});
});
});
describe('cancel', function () {
beforeEach(async function () {
this.operation = genOperation(
'0xC6837c44AA376dbe1d2709F13879E040CAb653ca',
0,
'0x296e58dd',
ZERO_BYTES32,
'0xa2485763600634800df9fc9646fb2c112cf98649c55f63dd1d9c7d13a64399d9',
);
({ receipt: this.receipt, logs: this.logs } = await this.timelock.schedule(
this.operation.target,
this.operation.value,
this.operation.data,
this.operation.predecessor,
this.operation.salt,
MINDELAY,
{ from: proposer },
));
});
it('proposer can cancel', async function () {
const receipt = await this.timelock.cancel(this.operation.id, { from: proposer });
expectEvent(receipt, 'Cancelled', { id: this.operation.id });
});
it('prevent non-proposer from canceling', async function () {
await expectRevert(
this.timelock.cancel(this.operation.id, { from: other }),
'TimelockController: sender requires permission',
);
});
});
});
describe('maintenance', function () {
it('prevent unauthorized maintenance', async function () {
await expectRevert(
this.timelock.updateDelay(0, { from: other }),
'TimelockController: caller must be timelock',
);
});
it('timelock scheduled maintenance', async function () {
const newDelay = time.duration.hours(6);
const operation = genOperation(
this.timelock.address,
0,
this.timelock.contract.methods.updateDelay(newDelay.toString()).encodeABI(),
ZERO_BYTES32,
'0xf8e775b2c5f4d66fb5c7fa800f35ef518c262b6014b3c0aee6ea21bff157f108',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
const receipt = await this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
);
expectEvent(receipt, 'MinDelayChange', { newDuration: newDelay.toString(), oldDuration: MINDELAY });
expect(await this.timelock.getMinDelay()).to.be.bignumber.equal(newDelay);
});
});
describe('dependency', function () {
beforeEach(async function () {
this.operation1 = genOperation(
'0xdE66bD4c97304200A95aE0AadA32d6d01A867E39',
0,
'0x01dc731a',
ZERO_BYTES32,
'0x64e932133c7677402ead2926f86205e2ca4686aebecf5a8077627092b9bb2feb',
);
this.operation2 = genOperation(
'0x3c7944a3F1ee7fc8c5A5134ba7c79D11c3A1FCa3',
0,
'0x8f531849',
this.operation1.id,
'0x036e1311cac523f9548e6461e29fb1f8f9196b91910a41711ea22f5de48df07d',
);
await this.timelock.schedule(
this.operation1.target,
this.operation1.value,
this.operation1.data,
this.operation1.predecessor,
this.operation1.salt,
MINDELAY,
{ from: proposer },
);
await this.timelock.schedule(
this.operation2.target,
this.operation2.value,
this.operation2.data,
this.operation2.predecessor,
this.operation2.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
});
it('cannot execute before dependency', async function () {
await expectRevert(
this.timelock.execute(
this.operation2.target,
this.operation2.value,
this.operation2.data,
this.operation2.predecessor,
this.operation2.salt,
{ from: executor },
),
'TimelockController: missing dependency',
);
});
it('can execute after dependency', async function () {
await this.timelock.execute(
this.operation1.target,
this.operation1.value,
this.operation1.data,
this.operation1.predecessor,
this.operation1.salt,
{ from: executor },
);
await this.timelock.execute(
this.operation2.target,
this.operation2.value,
this.operation2.data,
this.operation2.predecessor,
this.operation2.salt,
{ from: executor },
);
});
});
describe('usage scenario', function () {
this.timeout(10000);
it('call', async function () {
const operation = genOperation(
this.implementation2.address,
0,
this.implementation2.contract.methods.setValue(42).encodeABI(),
ZERO_BYTES32,
'0x8043596363daefc89977b25f9d9b4d06c3910959ef0c4d213557a903e1b555e2',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
await this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
);
expect(await this.implementation2.getValue()).to.be.bignumber.equal(web3.utils.toBN(42));
});
it('call reverting', async function () {
const operation = genOperation(
this.callreceivermock.address,
0,
this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
ZERO_BYTES32,
'0xb1b1b276fdf1a28d1e00537ea73b04d56639128b08063c1a2f70a52e38cba693',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
await expectRevert(
this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
),
'TimelockController: underlying transaction reverted',
);
});
it('call throw', async function () {
const operation = genOperation(
this.callreceivermock.address,
0,
this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(),
ZERO_BYTES32,
'0xe5ca79f295fc8327ee8a765fe19afb58f4a0cbc5053642bfdd7e73bc68e0fc67',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
await expectRevert(
this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
),
'TimelockController: underlying transaction reverted',
);
});
it('call out of gas', async function () {
const operation = genOperation(
this.callreceivermock.address,
0,
this.callreceivermock.contract.methods.mockFunctionOutOfGas().encodeABI(),
ZERO_BYTES32,
'0xf3274ce7c394c5b629d5215723563a744b817e1730cca5587c567099a14578fd',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
await expectRevert(
this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor, gas: '70000' },
),
'TimelockController: underlying transaction reverted',
);
});
it('call payable with eth', async function () {
const operation = genOperation(
this.callreceivermock.address,
1,
this.callreceivermock.contract.methods.mockFunction().encodeABI(),
ZERO_BYTES32,
'0x5ab73cd33477dcd36c1e05e28362719d0ed59a7b9ff14939de63a43073dc1f44',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
await this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor, value: 1 },
);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(1));
});
it('call nonpayable with eth', async function () {
const operation = genOperation(
this.callreceivermock.address,
1,
this.callreceivermock.contract.methods.mockFunctionNonPayable().encodeABI(),
ZERO_BYTES32,
'0xb78edbd920c7867f187e5aa6294ae5a656cfbf0dea1ccdca3751b740d0f2bdf8',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
await expectRevert(
this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
),
'TimelockController: underlying transaction reverted',
);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
});
it('call reverting with eth', async function () {
const operation = genOperation(
this.callreceivermock.address,
1,
this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(),
ZERO_BYTES32,
'0xdedb4563ef0095db01d81d3f2decf57cf83e4a72aa792af14c43a792b56f4de6',
);
await this.timelock.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
MINDELAY,
{ from: proposer },
);
await time.increase(MINDELAY);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
await expectRevert(
this.timelock.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
{ from: executor },
),
'TimelockController: underlying transaction reverted',
);
expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0));
});
});
});

@ -1,16 +1,14 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { toEthSignedMessageHash, fixSignature } = require('../helpers/sign');
const { expect } = require('chai');
const ECDSAMock = contract.fromArtifact('ECDSAMock');
const ECDSAMock = artifacts.require('ECDSAMock');
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
const WRONG_MESSAGE = web3.utils.sha3('Nope');
describe('ECDSA', function () {
contract('ECDSA', function (accounts) {
const [ other ] = accounts;
beforeEach(async function () {
@ -26,7 +24,7 @@ describe('ECDSA', function () {
await expectRevert(
// eslint-disable-next-line max-len
this.ecdsa.recover(TEST_MESSAGE, '0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'),
'ECDSA: invalid signature length'
'ECDSA: invalid signature length',
);
});
});
@ -116,7 +114,7 @@ describe('ECDSA', function () {
// Recover the signer address from the generated message and signature.
expect(await this.ecdsa.recover(
toEthSignedMessageHash(TEST_MESSAGE),
signature
signature,
)).to.equal(other);
});
});

@ -1,5 +1,3 @@
const { contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers');
const { MerkleTree } = require('../helpers/merkleTree.js');
@ -7,9 +5,9 @@ const { keccakFromString, bufferToHex } = require('ethereumjs-util');
const { expect } = require('chai');
const MerkleProofWrapper = contract.fromArtifact('MerkleProofWrapper');
const MerkleProofWrapper = artifacts.require('MerkleProofWrapper');
describe('MerkleProof', function () {
contract('MerkleProof', function (accounts) {
beforeEach(async function () {
this.merkleProof = await MerkleProofWrapper.new();
});

@ -0,0 +1,15 @@
const { GSNDevProvider } = require('@openzeppelin/gsn-provider');
function setGSNProvider (Contract, accounts) {
const baseProvider = Contract.currentProvider;
Contract.setProvider(
new GSNDevProvider(baseProvider, {
txfee: 70,
useGSN: false,
ownerAddress: accounts[8],
relayerAddress: accounts[9],
}),
);
};
module.exports = { setGSNProvider };

@ -1,5 +1,3 @@
const { web3 } = require('@openzeppelin/test-environment');
function toEthSignedMessageHash (messageHex) {
const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
@ -47,7 +45,7 @@ const getSignFor = (contract, signer) => (redeemer, methodName, methodArgs = [])
if (methodArgs.length > 0) {
parts.push(
contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE])).encodeABI()
.slice(0, -1 * PADDED_SIGNATURE_SIZE)
.slice(0, -1 * PADDED_SIGNATURE_SIZE),
);
} else {
const abi = contract.abi.find(abi => abi.name === methodName);

@ -1,11 +1,10 @@
const { contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { shouldSupportInterfaces } = require('./SupportsInterface.behavior');
const ERC165Mock = contract.fromArtifact('ERC165Mock');
const ERC165Mock = artifacts.require('ERC165Mock');
describe('ERC165', function () {
contract('ERC165', function (accounts) {
beforeEach(async function () {
this.mock = await ERC165Mock.new();
});

@ -1,11 +1,10 @@
const { contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC165CheckerMock = contract.fromArtifact('ERC165CheckerMock');
const ERC165NotSupported = contract.fromArtifact('ERC165NotSupported');
const ERC165InterfacesSupported = contract.fromArtifact('ERC165InterfacesSupported');
const ERC165CheckerMock = artifacts.require('ERC165CheckerMock');
const ERC165NotSupported = artifacts.require('ERC165NotSupported');
const ERC165InterfacesSupported = artifacts.require('ERC165InterfacesSupported');
const DUMMY_ID = '0xdeadbeef';
const DUMMY_ID_2 = '0xcafebabe';
@ -14,7 +13,7 @@ const DUMMY_UNSUPPORTED_ID = '0xbaddcafe';
const DUMMY_UNSUPPORTED_ID_2 = '0xbaadcafe';
const DUMMY_ACCOUNT = '0x1111111111111111111111111111111111111111';
describe('ERC165Checker', function () {
contract('ERC165Checker', function (accounts) {
beforeEach(async function () {
this.mock = await ERC165CheckerMock.new();
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert, singletons } = require('@openzeppelin/test-helpers');
const { bufferToHex, keccakFromString } = require('ethereumjs-util');
const { expect } = require('chai');
const ERC1820ImplementerMock = contract.fromArtifact('ERC1820ImplementerMock');
const ERC1820ImplementerMock = artifacts.require('ERC1820ImplementerMock');
describe('ERC1820Implementer', function () {
contract('ERC1820Implementer', function (accounts) {
const [ registryFunder, implementee, other ] = accounts;
const ERC1820_ACCEPT_MAGIC = bufferToHex(keccakFromString('ERC1820_ACCEPT_MAGIC'));
@ -29,9 +27,9 @@ describe('ERC1820Implementer', function () {
it('reverts when attempting to set as implementer in the registry', async function () {
await expectRevert(
this.registry.setInterfaceImplementer(
implementee, this.interfaceA, this.implementer.address, { from: implementee }
implementee, this.interfaceA, this.implementer.address, { from: implementee },
),
'Does not implement the interface'
'Does not implement the interface',
);
});
});
@ -58,7 +56,7 @@ describe('ERC1820Implementer', function () {
it('can be set as an implementer for supported interfaces in the registry', async function () {
await this.registry.setInterfaceImplementer(
implementee, this.interfaceA, this.implementer.address, { from: implementee }
implementee, this.interfaceA, this.implementer.address, { from: implementee },
);
expect(await this.registry.getInterfaceImplementer(implementee, this.interfaceA))

@ -35,6 +35,10 @@ const INTERFACES = {
'safeTransferFrom(address,address,uint256,uint256,bytes)',
'safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)',
],
ERC1155Receiver: [
'onERC1155Received(address,address,uint256,uint256,bytes)',
'onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)',
],
};
const INTERFACE_IDS = {};
@ -50,7 +54,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) {
function shouldSupportInterfaces (interfaces = []) {
describe('Contract interface', function () {
beforeEach(function () {
this.contractUnderTest = this.mock || this.token;
this.contractUnderTest = this.mock || this.token || this.holder;
});
for (const k of interfaces) {

@ -1,11 +1,10 @@
const { contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const MathMock = contract.fromArtifact('MathMock');
const MathMock = artifacts.require('MathMock');
describe('Math', function () {
contract('Math', function (accounts) {
const min = new BN('1234');
const max = new BN('5678');

@ -1,12 +1,11 @@
const { contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { MAX_UINT256 } = constants;
const { expect } = require('chai');
const SafeMathMock = contract.fromArtifact('SafeMathMock');
const SafeMathMock = artifacts.require('SafeMathMock');
describe('SafeMath', function () {
contract('SafeMath', function (accounts) {
beforeEach(async function () {
this.safeMath = await SafeMathMock.new();
});

@ -1,13 +1,11 @@
const { contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { MAX_INT256, MIN_INT256 } = constants;
const { expect } = require('chai');
const SignedSafeMathMock = contract.fromArtifact('SignedSafeMathMock');
const SignedSafeMathMock = artifacts.require('SignedSafeMathMock');
describe('SignedSafeMath', function () {
contract('SignedSafeMath', function (accounts) {
beforeEach(async function () {
this.safeMath = await SignedSafeMathMock.new();
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, constants, ether, expectEvent, send, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const PaymentSplitter = contract.fromArtifact('PaymentSplitter');
const PaymentSplitter = artifacts.require('PaymentSplitter');
describe('PaymentSplitter', function () {
contract('PaymentSplitter', function (accounts) {
const [ owner, payee1, payee2, payee3, nonpayee1, payer1 ] = accounts;
const amount = ether('1');
@ -18,31 +16,31 @@ describe('PaymentSplitter', function () {
it('rejects more payees than shares', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2, payee3], [20, 30]),
'PaymentSplitter: payees and shares length mismatch'
'PaymentSplitter: payees and shares length mismatch',
);
});
it('rejects more shares than payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 30, 40]),
'PaymentSplitter: payees and shares length mismatch'
'PaymentSplitter: payees and shares length mismatch',
);
});
it('rejects null payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, ZERO_ADDRESS], [20, 30]),
'PaymentSplitter: account is the zero address'
'PaymentSplitter: account is the zero address',
);
});
it('rejects zero-valued shares', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 0]),
'PaymentSplitter: shares are 0'
'PaymentSplitter: shares are 0',
);
});
it('rejects repeated payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee1], [20, 30]),
'PaymentSplitter: account already has shares'
'PaymentSplitter: account already has shares',
);
});
@ -84,13 +82,13 @@ describe('PaymentSplitter', function () {
describe('release', async function () {
it('reverts if no funds to claim', async function () {
await expectRevert(this.contract.release(payee1),
'PaymentSplitter: account is not due payment'
'PaymentSplitter: account is not due payment',
);
});
it('reverts if non-payee want to claim', async function () {
await send.ether(payer1, this.contract.address, amount);
await expectRevert(this.contract.release(nonpayee1),
'PaymentSplitter: account has no shares'
'PaymentSplitter: account has no shares',
);
});
});

@ -1,12 +1,10 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, ether } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const PullPaymentMock = contract.fromArtifact('PullPaymentMock');
const PullPaymentMock = artifacts.require('PullPaymentMock');
describe('PullPayment', function () {
contract('PullPayment', function (accounts) {
const [ payer, payee1, payee2 ] = accounts;
const amount = ether('17');

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { ether, expectRevert } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeEscrow } = require('./Escrow.behavior');
const ConditionalEscrowMock = contract.fromArtifact('ConditionalEscrowMock');
const ConditionalEscrowMock = artifacts.require('ConditionalEscrowMock');
describe('ConditionalEscrow', function () {
contract('ConditionalEscrow', function (accounts) {
const [ owner, payee, ...otherAccounts ] = accounts;
beforeEach(async function () {
@ -31,7 +29,7 @@ describe('ConditionalEscrow', function () {
await this.escrow.deposit(payee, { from: owner, value: amount });
await expectRevert(this.escrow.withdraw(payee, { from: owner }),
'ConditionalEscrow: payee is not allowed to withdraw'
'ConditionalEscrow: payee is not allowed to withdraw',
);
});
});

@ -21,7 +21,7 @@ function shouldBehaveLikeEscrow (owner, [payee1, payee2]) {
it('only the owner can deposit', async function () {
await expectRevert(this.escrow.deposit(payee1, { from: payee2 }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
});
@ -73,7 +73,7 @@ function shouldBehaveLikeEscrow (owner, [payee1, payee2]) {
it('only the owner can withdraw', async function () {
await expectRevert(this.escrow.withdraw(payee1, { from: payee1 }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
});

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers');
const { shouldBehaveLikeEscrow } = require('./Escrow.behavior');
const Escrow = contract.fromArtifact('Escrow');
const Escrow = artifacts.require('Escrow');
describe('Escrow', function () {
contract('Escrow', function (accounts) {
const [ owner, ...otherAccounts ] = accounts;
beforeEach(async function () {

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const RefundEscrow = contract.fromArtifact('RefundEscrow');
const RefundEscrow = artifacts.require('RefundEscrow');
describe('RefundEscrow', function () {
contract('RefundEscrow', function (accounts) {
const [ owner, beneficiary, refundee1, refundee2 ] = accounts;
const amount = ether('54');
@ -15,7 +13,7 @@ describe('RefundEscrow', function () {
it('requires a non-null beneficiary', async function () {
await expectRevert(
RefundEscrow.new(ZERO_ADDRESS, { from: owner }), 'RefundEscrow: beneficiary is the zero address'
RefundEscrow.new(ZERO_ADDRESS, { from: owner }), 'RefundEscrow: beneficiary is the zero address',
);
});
@ -39,21 +37,21 @@ describe('RefundEscrow', function () {
it('does not refund refundees', async function () {
await this.escrow.deposit(refundee1, { from: owner, value: amount });
await expectRevert(this.escrow.withdraw(refundee1),
'ConditionalEscrow: payee is not allowed to withdraw'
'ConditionalEscrow: payee is not allowed to withdraw',
);
});
it('does not allow beneficiary withdrawal', async function () {
await this.escrow.deposit(refundee1, { from: owner, value: amount });
await expectRevert(this.escrow.beneficiaryWithdraw(),
'RefundEscrow: beneficiary can only withdraw while closed'
'RefundEscrow: beneficiary can only withdraw while closed',
);
});
});
it('only the owner can enter closed state', async function () {
await expectRevert(this.escrow.close({ from: beneficiary }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
const { logs } = await this.escrow.close({ from: owner });
@ -69,13 +67,13 @@ describe('RefundEscrow', function () {
it('rejects deposits', async function () {
await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }),
'RefundEscrow: can only deposit while active'
'RefundEscrow: can only deposit while active',
);
});
it('does not refund refundees', async function () {
await expectRevert(this.escrow.withdraw(refundee1),
'ConditionalEscrow: payee is not allowed to withdraw'
'ConditionalEscrow: payee is not allowed to withdraw',
);
});
@ -87,20 +85,20 @@ describe('RefundEscrow', function () {
it('prevents entering the refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: owner }),
'RefundEscrow: can only enable refunds while active'
'RefundEscrow: can only enable refunds while active',
);
});
it('prevents re-entering the closed state', async function () {
await expectRevert(this.escrow.close({ from: owner }),
'RefundEscrow: can only close while active'
'RefundEscrow: can only close while active',
);
});
});
it('only the owner can enter refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: beneficiary }),
'Ownable: caller is not the owner'
'Ownable: caller is not the owner',
);
const { logs } = await this.escrow.enableRefunds({ from: owner });
@ -116,7 +114,7 @@ describe('RefundEscrow', function () {
it('rejects deposits', async function () {
await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }),
'RefundEscrow: can only deposit while active'
'RefundEscrow: can only deposit while active',
);
});
@ -130,19 +128,19 @@ describe('RefundEscrow', function () {
it('does not allow beneficiary withdrawal', async function () {
await expectRevert(this.escrow.beneficiaryWithdraw(),
'RefundEscrow: beneficiary can only withdraw while closed'
'RefundEscrow: beneficiary can only withdraw while closed',
);
});
it('prevents entering the closed state', async function () {
await expectRevert(this.escrow.close({ from: owner }),
'RefundEscrow: can only close while active'
'RefundEscrow: can only close while active',
);
});
it('prevents re-entering the refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: owner }),
'RefundEscrow: can only enable refunds while active'
'RefundEscrow: can only enable refunds while active',
);
});
});

@ -1,13 +1,11 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC1155PresetMinterPauser = contract.fromArtifact('ERC1155PresetMinterPauser');
const ERC1155PresetMinterPauser = artifacts.require('ERC1155PresetMinterPauser');
describe('ERC1155PresetMinterPauser', function () {
contract('ERC1155PresetMinterPauser', function (accounts) {
const [ deployer, other ] = accounts;
const firstTokenId = new BN('845');
@ -50,7 +48,7 @@ describe('ERC1155PresetMinterPauser', function () {
it('deployer can mint tokens', async function () {
const receipt = await this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer });
expectEvent(receipt, 'TransferSingle',
{ operator: deployer, from: ZERO_ADDRESS, to: other, value: firstTokenIdAmount, id: firstTokenId }
{ operator: deployer, from: ZERO_ADDRESS, to: other, value: firstTokenIdAmount, id: firstTokenId },
);
expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount);
@ -59,7 +57,7 @@ describe('ERC1155PresetMinterPauser', function () {
it('other accounts cannot mint tokens', async function () {
await expectRevert(
this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: other }),
'ERC1155PresetMinterPauser: must have minter role to mint'
'ERC1155PresetMinterPauser: must have minter role to mint',
);
});
});
@ -67,11 +65,11 @@ describe('ERC1155PresetMinterPauser', function () {
describe('batched minting', function () {
it('deployer can batch mint tokens', async function () {
const receipt = await this.token.mintBatch(
other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: deployer }
other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: deployer },
);
expectEvent(receipt, 'TransferBatch',
{ operator: deployer, from: ZERO_ADDRESS, to: other }
{ operator: deployer, from: ZERO_ADDRESS, to: other },
);
expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount);
@ -80,9 +78,9 @@ describe('ERC1155PresetMinterPauser', function () {
it('other accounts cannot batch mint tokens', async function () {
await expectRevert(
this.token.mintBatch(
other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: other }
other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: other },
),
'ERC1155PresetMinterPauser: must have minter role to mint'
'ERC1155PresetMinterPauser: must have minter role to mint',
);
});
});
@ -109,14 +107,14 @@ describe('ERC1155PresetMinterPauser', function () {
await expectRevert(
this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('other accounts cannot pause', async function () {
await expectRevert(
this.token.pause({ from: other }),
'ERC1155PresetMinterPauser: must have pauser role to pause'
'ERC1155PresetMinterPauser: must have pauser role to pause',
);
});
});
@ -127,7 +125,7 @@ describe('ERC1155PresetMinterPauser', function () {
const receipt = await this.token.burn(other, firstTokenId, firstTokenIdAmount.subn(1), { from: other });
expectEvent(receipt, 'TransferSingle',
{ operator: other, from: other, to: ZERO_ADDRESS, value: firstTokenIdAmount.subn(1), id: firstTokenId }
{ operator: other, from: other, to: ZERO_ADDRESS, value: firstTokenIdAmount.subn(1), id: firstTokenId },
);
expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal('1');

@ -1,13 +1,11 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC20PresetMinterPauser = contract.fromArtifact('ERC20PresetMinterPauser');
const ERC20PresetMinterPauser = artifacts.require('ERC20PresetMinterPauser');
describe('ERC20PresetMinterPauser', function () {
contract('ERC20PresetMinterPauser', function (accounts) {
const [ deployer, other ] = accounts;
const name = 'MinterPauserToken';
@ -54,7 +52,7 @@ describe('ERC20PresetMinterPauser', function () {
it('other accounts cannot mint tokens', async function () {
await expectRevert(
this.token.mint(other, amount, { from: other }),
'ERC20PresetMinterPauser: must have minter role to mint'
'ERC20PresetMinterPauser: must have minter role to mint',
);
});
});
@ -81,7 +79,7 @@ describe('ERC20PresetMinterPauser', function () {
await expectRevert(
this.token.mint(other, amount, { from: deployer }),
'ERC20Pausable: token transfer while paused'
'ERC20Pausable: token transfer while paused',
);
});

@ -1,13 +1,11 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC721PresetMinterPauserAutoId = contract.fromArtifact('ERC721PresetMinterPauserAutoId');
const ERC721PresetMinterPauserAutoId = artifacts.require('ERC721PresetMinterPauserAutoId');
describe('ERC721PresetMinterPauserAutoId', function () {
contract('ERC721PresetMinterPauserAutoId', function (accounts) {
const [ deployer, other ] = accounts;
const name = 'MinterAutoIDToken';
@ -63,7 +61,7 @@ describe('ERC721PresetMinterPauserAutoId', function () {
it('other accounts cannot mint tokens', async function () {
await expectRevert(
this.token.mint(other, { from: other }),
'ERC721PresetMinterPauserAutoId: must have minter role to mint'
'ERC721PresetMinterPauserAutoId: must have minter role to mint',
);
});
});
@ -90,14 +88,14 @@ describe('ERC721PresetMinterPauserAutoId', function () {
await expectRevert(
this.token.mint(other, { from: deployer }),
'ERC721Pausable: token transfer while paused'
'ERC721Pausable: token transfer while paused',
);
});
it('other accounts cannot pause', async function () {
await expectRevert(
this.token.pause({ from: other }),
'ERC721PresetMinterPauserAutoId: must have pauser role to pause'
'ERC721PresetMinterPauserAutoId: must have pauser role to pause',
);
});
});

@ -1,13 +1,11 @@
const { contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { assert } = require('chai');
const InitializableMock = contract.fromArtifact('InitializableMock');
const SampleChild = contract.fromArtifact('SampleChild');
const InitializableMock = artifacts.require('InitializableMock');
const SampleChild = artifacts.require('SampleChild');
describe('Initializable', function () {
contract('Initializable', function (accounts) {
describe('basic testing without inheritance', function () {
beforeEach('deploying', async function () {
this.contract = await InitializableMock.new();

@ -1,15 +1,13 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ImplV1 = contract.fromArtifact('DummyImplementation');
const ImplV2 = contract.fromArtifact('DummyImplementationV2');
const ProxyAdmin = contract.fromArtifact('ProxyAdmin');
const TransparentUpgradeableProxy = contract.fromArtifact('TransparentUpgradeableProxy');
const ImplV1 = artifacts.require('DummyImplementation');
const ImplV2 = artifacts.require('DummyImplementationV2');
const ProxyAdmin = artifacts.require('ProxyAdmin');
const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy');
describe('ProxyAdmin', function () {
contract('ProxyAdmin', function (accounts) {
const [proxyAdminOwner, newAdmin, anotherAccount] = accounts;
before('set implementations', async function () {
@ -85,7 +83,7 @@ describe('ProxyAdmin', function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await expectRevert(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: anotherAccount }
{ from: anotherAccount },
),
'caller is not the owner',
);
@ -98,7 +96,7 @@ describe('ProxyAdmin', function () {
const callData = '0x12345678';
await expectRevert.unspecified(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner }
{ from: proxyAdminOwner },
),
);
});
@ -108,7 +106,7 @@ describe('ProxyAdmin', function () {
it('upgrades implementation', async function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner }
{ from: proxyAdminOwner },
);
const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address);
expect(implementationAddress).to.be.equal(this.implementationV2.address);

@ -1,22 +1,20 @@
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai');
const Proxy = contract.fromArtifact('Proxy');
const Implementation1 = contract.fromArtifact('Implementation1');
const Implementation2 = contract.fromArtifact('Implementation2');
const Implementation3 = contract.fromArtifact('Implementation3');
const Implementation4 = contract.fromArtifact('Implementation4');
const MigratableMockV1 = contract.fromArtifact('MigratableMockV1');
const MigratableMockV2 = contract.fromArtifact('MigratableMockV2');
const MigratableMockV3 = contract.fromArtifact('MigratableMockV3');
const InitializableMock = contract.fromArtifact('InitializableMock');
const DummyImplementation = contract.fromArtifact('DummyImplementation');
const ClashingImplementation = contract.fromArtifact('ClashingImplementation');
const Proxy = artifacts.require('Proxy');
const Implementation1 = artifacts.require('Implementation1');
const Implementation2 = artifacts.require('Implementation2');
const Implementation3 = artifacts.require('Implementation3');
const Implementation4 = artifacts.require('Implementation4');
const MigratableMockV1 = artifacts.require('MigratableMockV1');
const MigratableMockV2 = artifacts.require('MigratableMockV2');
const MigratableMockV3 = artifacts.require('MigratableMockV3');
const InitializableMock = artifacts.require('InitializableMock');
const DummyImplementation = artifacts.require('DummyImplementation');
const ClashingImplementation = artifacts.require('ClashingImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
const ADMIN_LABEL = 'eip1967.proxy.admin';
@ -89,7 +87,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
it('reverts', async function () {
await expectRevert.unspecified(
this.proxy.upgradeTo(this.implementationV1, { from })
this.proxy.upgradeTo(this.implementationV1, { from }),
);
});
});
@ -409,7 +407,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
await proxy.upgradeTo(instance4.address, { from: proxyAdminAddress });
const proxyInstance4 = new Implementation4(proxy.address);
const data = '';
const data = '0x';
await web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data });
const res = await proxyInstance4.getValue();
@ -423,7 +421,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
const instance2 = await Implementation2.new();
await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress });
const data = '';
const data = '0x';
await expectRevert.unspecified(
web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }),
);

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour');
const TransparentUpgradeableProxy = contract.fromArtifact('TransparentUpgradeableProxy');
const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy');
describe('TransparentUpgradeableProxy', function () {
contract('TransparentUpgradeableProxy', function (accounts) {
const [proxyAdminAddress, proxyAdminOwner] = accounts;
const createProxy = async function (logic, admin, initData, opts) {

@ -1,11 +1,9 @@
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai');
const DummyImplementation = contract.fromArtifact('DummyImplementation');
const DummyImplementation = artifacts.require('DummyImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';

@ -1,10 +1,8 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const UpgradeableProxy = contract.fromArtifact('UpgradeableProxy');
const UpgradeableProxy = artifacts.require('UpgradeableProxy');
describe('UpgradeableProxy', function () {
contract('UpgradeableProxy', function (accounts) {
const [proxyAdminOwner] = accounts;
const createProxy = async function (implementation, _admin, initData, opts) {

@ -1,6 +1,6 @@
const { defaultSender, web3 } = require('@openzeppelin/test-environment');
const { deployRelayHub } = require('@openzeppelin/gsn-helpers');
before('deploy GSN RelayHub', async function () {
const [defaultSender] = await web3.eth.getAccounts();
await deployRelayHub(web3, { from: defaultSender });
});

@ -1,5 +1,3 @@
const { contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
@ -7,7 +5,7 @@ const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
const ERC1155ReceiverMock = contract.fromArtifact('ERC1155ReceiverMock');
const ERC1155ReceiverMock = artifacts.require('ERC1155ReceiverMock');
function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder, multiTokenHolder, recipient, proxy]) {
const firstTokenId = new BN(1);
@ -25,7 +23,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
it('reverts when queried about the zero address', async function () {
await expectRevert(
this.token.balanceOf(ZERO_ADDRESS, firstTokenId),
'ERC1155: balance query for the zero address'
'ERC1155: balance query for the zero address',
);
});
@ -33,17 +31,17 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
it('returns zero for given addresses', async function () {
expect(await this.token.balanceOf(
firstTokenHolder,
firstTokenId
firstTokenId,
)).to.be.bignumber.equal('0');
expect(await this.token.balanceOf(
secondTokenHolder,
secondTokenId
secondTokenId,
)).to.be.bignumber.equal('0');
expect(await this.token.balanceOf(
firstTokenHolder,
unknownTokenId
unknownTokenId,
)).to.be.bignumber.equal('0');
});
});
@ -60,24 +58,24 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{
from: minter,
}
},
);
});
it('returns the amount of tokens owned by the given addresses', async function () {
expect(await this.token.balanceOf(
firstTokenHolder,
firstTokenId
firstTokenId,
)).to.be.bignumber.equal(firstAmount);
expect(await this.token.balanceOf(
secondTokenHolder,
secondTokenId
secondTokenId,
)).to.be.bignumber.equal(secondAmount);
expect(await this.token.balanceOf(
firstTokenHolder,
unknownTokenId
unknownTokenId,
)).to.be.bignumber.equal('0');
});
});
@ -88,17 +86,17 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
await expectRevert(
this.token.balanceOfBatch(
[firstTokenHolder, secondTokenHolder, firstTokenHolder, secondTokenHolder],
[firstTokenId, secondTokenId, unknownTokenId]
[firstTokenId, secondTokenId, unknownTokenId],
),
'ERC1155: accounts and ids length mismatch'
'ERC1155: accounts and ids length mismatch',
);
await expectRevert(
this.token.balanceOfBatch(
[firstTokenHolder, secondTokenHolder],
[firstTokenId, secondTokenId, unknownTokenId]
[firstTokenId, secondTokenId, unknownTokenId],
),
'ERC1155: accounts and ids length mismatch'
'ERC1155: accounts and ids length mismatch',
);
});
@ -106,9 +104,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
await expectRevert(
this.token.balanceOfBatch(
[firstTokenHolder, secondTokenHolder, ZERO_ADDRESS],
[firstTokenId, secondTokenId, unknownTokenId]
[firstTokenId, secondTokenId, unknownTokenId],
),
'ERC1155: batch balance query for the zero address'
'ERC1155: batch balance query for the zero address',
);
});
@ -116,7 +114,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
it('returns zeros for each account', async function () {
const result = await this.token.balanceOfBatch(
[firstTokenHolder, secondTokenHolder, firstTokenHolder],
[firstTokenId, secondTokenId, unknownTokenId]
[firstTokenId, secondTokenId, unknownTokenId],
);
expect(result).to.be.an('array');
expect(result[0]).to.be.a.bignumber.equal('0');
@ -137,14 +135,14 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{
from: minter,
}
},
);
});
it('returns amounts owned by each account in order passed', async function () {
const result = await this.token.balanceOfBatch(
[secondTokenHolder, firstTokenHolder, firstTokenHolder],
[secondTokenId, firstTokenId, unknownTokenId]
[secondTokenId, firstTokenId, unknownTokenId],
);
expect(result).to.be.an('array');
expect(result[0]).to.be.a.bignumber.equal(secondAmount);
@ -155,7 +153,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
it('returns multiple times the balance of the same address when asked', async function () {
const result = await this.token.balanceOfBatch(
[firstTokenHolder, secondTokenHolder, firstTokenHolder],
[firstTokenId, secondTokenId, firstTokenId]
[firstTokenId, secondTokenId, firstTokenId],
);
expect(result).to.be.an('array');
expect(result[0]).to.be.a.bignumber.equal(result[2]);
@ -188,7 +186,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
it('reverts if attempting to approve self as an operator', async function () {
await expectRevert(
this.token.setApprovalForAll(multiTokenHolder, true, { from: multiTokenHolder }),
'ERC1155: setting approval status for self'
'ERC1155: setting approval status for self',
);
});
});
@ -205,7 +203,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{
from: minter,
}
},
);
});
@ -219,7 +217,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{ from: multiTokenHolder },
),
'ERC1155: insufficient balance for transfer'
'ERC1155: insufficient balance for transfer',
);
});
@ -233,7 +231,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{ from: multiTokenHolder },
),
'ERC1155: transfer to the zero address'
'ERC1155: transfer to the zero address',
);
});
@ -295,7 +293,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstAmount, '0x', {
from: proxy,
}),
'ERC1155: caller is not owner nor approved'
'ERC1155: caller is not owner nor approved',
);
});
});
@ -344,7 +342,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
firstTokenId,
firstAmount,
'0x',
{ from: multiTokenHolder }
{ from: multiTokenHolder },
);
({ logs: this.transferLogs } = this.transferReceipt);
});
@ -377,7 +375,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
firstTokenId,
firstAmount,
data,
{ from: multiTokenHolder }
{ from: multiTokenHolder },
);
({ logs: this.transferLogs } = this.transferReceipt);
});
@ -414,7 +412,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
from: multiTokenHolder,
}),
'ERC1155: ERC1155Receiver rejected tokens'
'ERC1155: ERC1155Receiver rejected tokens',
);
});
});
@ -432,7 +430,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', {
from: multiTokenHolder,
}),
'ERC1155ReceiverMock: reverting on receive'
'ERC1155ReceiverMock: reverting on receive',
);
});
});
@ -443,7 +441,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
await expectRevert.unspecified(
this.token.safeTransferFrom(multiTokenHolder, invalidReceiver.address, firstTokenId, firstAmount, '0x', {
from: multiTokenHolder,
})
}),
);
});
});
@ -461,7 +459,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
'0x',
{
from: minter,
}
},
);
});
@ -471,9 +469,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, recipient,
[firstTokenId, secondTokenId],
[firstAmount, secondAmount.addn(1)],
'0x', { from: multiTokenHolder }
'0x', { from: multiTokenHolder },
),
'ERC1155: insufficient balance for transfer'
'ERC1155: insufficient balance for transfer',
);
});
@ -483,9 +481,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, recipient,
[firstTokenId],
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder }
'0x', { from: multiTokenHolder },
),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
await expectRevert(
@ -493,9 +491,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, recipient,
[firstTokenId, secondTokenId],
[firstAmount],
'0x', { from: multiTokenHolder }
'0x', { from: multiTokenHolder },
),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
});
@ -505,9 +503,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, ZERO_ADDRESS,
[firstTokenId, secondTokenId],
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder }
'0x', { from: multiTokenHolder },
),
'ERC1155: transfer to the zero address'
'ERC1155: transfer to the zero address',
);
});
@ -545,7 +543,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, recipient,
[firstTokenId, secondTokenId],
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder }
'0x', { from: multiTokenHolder },
));
});
@ -569,9 +567,9 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
multiTokenHolder, recipient,
[firstTokenId, secondTokenId],
[firstAmount, secondAmount],
'0x', { from: proxy }
'0x', { from: proxy },
),
'ERC1155: transfer caller is not owner nor approved'
'ERC1155: transfer caller is not owner nor approved',
);
});
});
@ -691,7 +689,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder },
),
'ERC1155: ERC1155Receiver rejected tokens'
'ERC1155: ERC1155Receiver rejected tokens',
);
});
});
@ -712,7 +710,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder },
),
'ERC1155ReceiverMock: reverting on batch receive'
'ERC1155ReceiverMock: reverting on batch receive',
);
});
});
@ -761,7 +759,7 @@ function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder,
[firstTokenId, secondTokenId],
[firstAmount, secondAmount],
'0x', { from: multiTokenHolder },
)
),
);
});
});

@ -1,14 +1,12 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior');
const ERC1155Mock = contract.fromArtifact('ERC1155Mock');
const ERC1155Mock = artifacts.require('ERC1155Mock');
describe('ERC1155', function () {
contract('ERC1155', function (accounts) {
const [operator, tokenHolder, tokenBatchHolder, ...otherAccounts] = accounts;
const initialURI = 'https://token-cdn-domain/{id}.json';
@ -34,7 +32,7 @@ describe('ERC1155', function () {
it('reverts with a zero destination address', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, tokenId, mintAmount, data),
'ERC1155: mint to the zero address'
'ERC1155: mint to the zero address',
);
});
@ -63,19 +61,19 @@ describe('ERC1155', function () {
it('reverts with a zero destination address', async function () {
await expectRevert(
this.token.mintBatch(ZERO_ADDRESS, tokenBatchIds, mintAmounts, data),
'ERC1155: mint to the zero address'
'ERC1155: mint to the zero address',
);
});
it('reverts if length of inputs do not match', async function () {
await expectRevert(
this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts.slice(1), data),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
await expectRevert(
this.token.mintBatch(tokenBatchHolder, tokenBatchIds.slice(1), mintAmounts, data),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
});
@ -86,7 +84,7 @@ describe('ERC1155', function () {
tokenBatchIds,
mintAmounts,
data,
{ from: operator }
{ from: operator },
));
});
@ -101,7 +99,7 @@ describe('ERC1155', function () {
it('credits the minted batch of tokens', async function () {
const holderBatchBalances = await this.token.balanceOfBatch(
new Array(tokenBatchIds.length).fill(tokenBatchHolder),
tokenBatchIds
tokenBatchIds,
);
for (let i = 0; i < holderBatchBalances.length; i++) {
@ -115,14 +113,14 @@ describe('ERC1155', function () {
it('reverts when burning the zero account\'s tokens', async function () {
await expectRevert(
this.token.burn(ZERO_ADDRESS, tokenId, mintAmount),
'ERC1155: burn from the zero address'
'ERC1155: burn from the zero address',
);
});
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burn(tokenHolder, tokenId, mintAmount),
'ERC1155: burn amount exceeds balance'
'ERC1155: burn amount exceeds balance',
);
});
@ -132,12 +130,12 @@ describe('ERC1155', function () {
tokenId,
mintAmount,
data,
{ from: operator }
{ from: operator },
);
await expectRevert(
this.token.burn(tokenHolder, tokenId, mintAmount.addn(1)),
'ERC1155: burn amount exceeds balance'
'ERC1155: burn amount exceeds balance',
);
});
@ -148,7 +146,7 @@ describe('ERC1155', function () {
tokenHolder,
tokenId,
burnAmount,
{ from: operator }
{ from: operator },
));
});
@ -165,7 +163,7 @@ describe('ERC1155', function () {
it('accounts for both minting and burning', async function () {
expect(await this.token.balanceOf(
tokenHolder,
tokenId
tokenId,
)).to.be.bignumber.equal(mintAmount.sub(burnAmount));
});
});
@ -175,26 +173,26 @@ describe('ERC1155', function () {
it('reverts when burning the zero account\'s tokens', async function () {
await expectRevert(
this.token.burnBatch(ZERO_ADDRESS, tokenBatchIds, burnAmounts),
'ERC1155: burn from the zero address'
'ERC1155: burn from the zero address',
);
});
it('reverts if length of inputs do not match', async function () {
await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts.slice(1)),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds.slice(1), burnAmounts),
'ERC1155: ids and amounts length mismatch'
'ERC1155: ids and amounts length mismatch',
);
});
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts),
'ERC1155: burn amount exceeds balance'
'ERC1155: burn amount exceeds balance',
);
});
@ -205,7 +203,7 @@ describe('ERC1155', function () {
tokenBatchHolder,
tokenBatchIds,
burnAmounts,
{ from: operator }
{ from: operator },
));
});
@ -222,7 +220,7 @@ describe('ERC1155', function () {
it('accounts for both minting and burning', async function () {
const holderBatchBalances = await this.token.balanceOfBatch(
new Array(tokenBatchIds.length).fill(tokenBatchHolder),
tokenBatchIds
tokenBatchIds,
);
for (let i = 0; i < holderBatchBalances.length; i++) {

@ -1,12 +1,10 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC1155BurnableMock = contract.fromArtifact('ERC1155BurnableMock');
const ERC1155BurnableMock = artifacts.require('ERC1155BurnableMock');
describe('ERC1155Burnable', function () {
contract('ERC1155Burnable', function (accounts) {
const [ holder, operator, other ] = accounts;
const uri = 'https://token.com';
@ -38,7 +36,7 @@ describe('ERC1155Burnable', function () {
it('unapproved accounts cannot burn the holder\'s tokens', async function () {
await expectRevert(
this.token.burn(holder, tokenIds[0], amounts[0].subn(1), { from: other }),
'ERC1155: caller is not owner nor approved'
'ERC1155: caller is not owner nor approved',
);
});
});
@ -62,7 +60,7 @@ describe('ERC1155Burnable', function () {
it('unapproved accounts cannot burn the holder\'s tokens', async function () {
await expectRevert(
this.token.burnBatch(holder, tokenIds, [ amounts[0].subn(1), amounts[1].subn(2) ], { from: other }),
'ERC1155: caller is not owner nor approved'
'ERC1155: caller is not owner nor approved',
);
});
});

@ -1,12 +1,13 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers');
const ERC1155Holder = contract.fromArtifact('ERC1155Holder');
const ERC1155Mock = contract.fromArtifact('ERC1155Mock');
const ERC1155Holder = artifacts.require('ERC1155Holder');
const ERC1155Mock = artifacts.require('ERC1155Mock');
const { expect } = require('chai');
describe('ERC1155Holder', function () {
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
contract('ERC1155Holder', function (accounts) {
const [creator] = accounts;
const uri = 'https://token-cdn-domain/{id}.json';
const multiTokenIds = [new BN(1), new BN(2), new BN(3)];
@ -19,6 +20,8 @@ describe('ERC1155Holder', function () {
await this.multiToken.mintBatch(creator, multiTokenIds, multiTokenAmounts, '0x', { from: creator });
});
shouldSupportInterfaces(['ERC165', 'ERC1155Receiver']);
it('receives ERC1155 tokens from a single ID', async function () {
await this.multiToken.safeTransferFrom(
creator,

@ -1,12 +1,10 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC1155PausableMock = contract.fromArtifact('ERC1155PausableMock');
const ERC1155PausableMock = artifacts.require('ERC1155PausableMock');
describe('ERC1155Pausable', function () {
contract('ERC1155Pausable', function (accounts) {
const [ holder, operator, receiver, other ] = accounts;
const uri = 'https://token.com';
@ -32,58 +30,58 @@ describe('ERC1155Pausable', function () {
it('reverts when trying to safeTransferFrom from holder', async function () {
await expectRevert(
this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: holder }),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to safeTransferFrom from operator', async function () {
await expectRevert(
this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: operator }),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to safeBatchTransferFrom from holder', async function () {
await expectRevert(
this.token.safeBatchTransferFrom(holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: holder }),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to safeBatchTransferFrom from operator', async function () {
await expectRevert(
this.token.safeBatchTransferFrom(
holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: operator }
holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: operator },
),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to mint', async function () {
await expectRevert(
this.token.mint(holder, secondTokenId, secondTokenAmount, '0x'),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to mintBatch', async function () {
await expectRevert(
this.token.mintBatch(holder, [secondTokenId], [secondTokenAmount], '0x'),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to burn', async function () {
await expectRevert(
this.token.burn(holder, firstTokenId, firstTokenAmount),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});
it('reverts when trying to burnBatch', async function () {
await expectRevert(
this.token.burn(holder, [firstTokenId], [firstTokenAmount]),
'ERC1155Pausable: token transfer while paused'
'ERC1155Pausable: token transfer while paused',
);
});

@ -27,7 +27,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply,
function (from, to, value) {
return this.token.transfer(to, value, { from });
}
},
);
});
@ -88,7 +88,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(this.token.transferFrom(
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`,
);
});
});
@ -104,7 +104,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(this.token.transferFrom(
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds allowance`
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds allowance`,
);
});
});
@ -114,7 +114,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(this.token.transferFrom(
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer amount exceeds balance`,
);
});
});
@ -131,7 +131,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(this.token.transferFrom(
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer to the zero address`
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer to the zero address`,
);
});
});
@ -144,7 +144,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () {
await expectRevert(this.token.transferFrom(
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer from the zero address`
tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer from the zero address`,
);
});
});
@ -154,7 +154,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply,
function (owner, spender, amount) {
return this.token.approve(spender, amount, { from: owner });
}
},
);
});
}
@ -166,7 +166,7 @@ function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer
it('reverts', async function () {
await expectRevert(transfer.call(this, from, to, amount),
`${errorPrefix}: transfer amount exceeds balance`
`${errorPrefix}: transfer amount exceeds balance`,
);
});
});
@ -219,7 +219,7 @@ function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer
describe('when the recipient is the zero address', function () {
it('reverts', async function () {
await expectRevert(transfer.call(this, from, ZERO_ADDRESS, balance),
`${errorPrefix}: transfer to the zero address`
`${errorPrefix}: transfer to the zero address`,
);
});
});
@ -299,7 +299,7 @@ function shouldBehaveLikeERC20Approve (errorPrefix, owner, spender, supply, appr
describe('when the spender is the zero address', function () {
it('reverts', async function () {
await expectRevert(approve.call(this, owner, ZERO_ADDRESS, supply),
`${errorPrefix}: approve to the zero address`
`${errorPrefix}: approve to the zero address`,
);
});
});

@ -1,5 +1,3 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { ZERO_ADDRESS } = constants;
@ -10,10 +8,10 @@ const {
shouldBehaveLikeERC20Approve,
} = require('./ERC20.behavior');
const ERC20Mock = contract.fromArtifact('ERC20Mock');
const ERC20DecimalsMock = contract.fromArtifact('ERC20DecimalsMock');
const ERC20Mock = artifacts.require('ERC20Mock');
const ERC20DecimalsMock = artifacts.require('ERC20DecimalsMock');
describe('ERC20', function () {
contract('ERC20', function (accounts) {
const [ initialHolder, recipient, anotherAccount ] = accounts;
const name = 'My Token';
@ -56,7 +54,7 @@ describe('ERC20', function () {
describe('when there was no approved amount before', function () {
it('reverts', async function () {
await expectRevert(this.token.decreaseAllowance(
spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero'
spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero',
);
});
});
@ -92,7 +90,7 @@ describe('ERC20', function () {
it('reverts when more than the full allowance is removed', async function () {
await expectRevert(
this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }),
'ERC20: decreased allowance below zero'
'ERC20: decreased allowance below zero',
);
});
});
@ -117,7 +115,7 @@ describe('ERC20', function () {
it('reverts', async function () {
await expectRevert(this.token.decreaseAllowance(
spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero'
spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero',
);
});
});
@ -201,7 +199,7 @@ describe('ERC20', function () {
it('reverts', async function () {
await expectRevert(
this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'ERC20: approve to the zero address'
this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'ERC20: approve to the zero address',
);
});
});
@ -211,7 +209,7 @@ describe('ERC20', function () {
const amount = new BN(50);
it('rejects a null account', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, amount), 'ERC20: mint to the zero address'
this.token.mint(ZERO_ADDRESS, amount), 'ERC20: mint to the zero address',
);
});
@ -250,7 +248,7 @@ describe('ERC20', function () {
describe('for a non zero account', function () {
it('rejects burning more than balance', async function () {
await expectRevert(this.token.burn(
initialHolder, initialSupply.addn(1)), 'ERC20: burn amount exceeds balance'
initialHolder, initialSupply.addn(1)), 'ERC20: burn amount exceeds balance',
);
});
@ -295,7 +293,7 @@ describe('ERC20', function () {
describe('when the sender is the zero address', function () {
it('reverts', async function () {
await expectRevert(this.token.transferInternal(ZERO_ADDRESS, recipient, initialSupply),
'ERC20: transfer from the zero address'
'ERC20: transfer from the zero address',
);
});
});
@ -309,7 +307,7 @@ describe('ERC20', function () {
describe('when the owner is the zero address', function () {
it('reverts', async function () {
await expectRevert(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply),
'ERC20: approve from the zero address'
'ERC20: approve from the zero address',
);
});
});

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Burnable } = require('./behaviors/ERC20Burnable.behavior');
const ERC20BurnableMock = contract.fromArtifact('ERC20BurnableMock');
const ERC20BurnableMock = artifacts.require('ERC20BurnableMock');
describe('ERC20Burnable', function () {
contract('ERC20Burnable', function (accounts) {
const [ owner, ...otherAccounts ] = accounts;
const initialBalance = new BN(1000);

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Capped } = require('./behaviors/ERC20Capped.behavior');
const ERC20Capped = contract.fromArtifact('ERC20CappedMock');
const ERC20Capped = artifacts.require('ERC20CappedMock');
describe('ERC20Capped', function () {
contract('ERC20Capped', function (accounts) {
const [ minter, ...otherAccounts ] = accounts;
const cap = ether('1000');
@ -15,7 +13,7 @@ describe('ERC20Capped', function () {
it('requires a non-zero cap', async function () {
await expectRevert(
ERC20Capped.new(name, symbol, new BN(0), { from: minter }), 'ERC20Capped: cap is 0'
ERC20Capped.new(name, symbol, new BN(0), { from: minter }), 'ERC20Capped: cap is 0',
);
});

@ -1,12 +1,10 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC20PausableMock = contract.fromArtifact('ERC20PausableMock');
const ERC20PausableMock = artifacts.require('ERC20PausableMock');
describe('ERC20Pausable', function () {
contract('ERC20Pausable', function (accounts) {
const [ holder, recipient, anotherAccount ] = accounts;
const initialSupply = new BN(100);
@ -41,7 +39,7 @@ describe('ERC20Pausable', function () {
await this.token.pause();
await expectRevert(this.token.transfer(recipient, initialSupply, { from: holder }),
'ERC20Pausable: token transfer while paused'
'ERC20Pausable: token transfer while paused',
);
});
});
@ -74,7 +72,7 @@ describe('ERC20Pausable', function () {
await this.token.pause();
await expectRevert(this.token.transferFrom(
holder, recipient, allowance, { from: anotherAccount }), 'ERC20Pausable: token transfer while paused'
holder, recipient, allowance, { from: anotherAccount }), 'ERC20Pausable: token transfer while paused',
);
});
});
@ -101,7 +99,7 @@ describe('ERC20Pausable', function () {
await this.token.pause();
await expectRevert(this.token.mint(recipient, amount),
'ERC20Pausable: token transfer while paused'
'ERC20Pausable: token transfer while paused',
);
});
});
@ -128,7 +126,7 @@ describe('ERC20Pausable', function () {
await this.token.pause();
await expectRevert(this.token.burn(holder, amount),
'ERC20Pausable: token transfer while paused'
'ERC20Pausable: token transfer while paused',
);
});
});

@ -1,11 +1,9 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const ERC20SnapshotMock = contract.fromArtifact('ERC20SnapshotMock');
const ERC20SnapshotMock = artifacts.require('ERC20SnapshotMock');
const { expect } = require('chai');
describe('ERC20Snapshot', function () {
contract('ERC20Snapshot', function (accounts) {
const [ initialHolder, recipient, other ] = accounts;
const initialSupply = new BN(100);
@ -76,7 +74,7 @@ describe('ERC20Snapshot', function () {
expect(await this.token.totalSupplyAt(this.initialSnapshotId)).to.be.bignumber.equal(initialSupply);
expect(await this.token.totalSupplyAt(this.secondSnapshotId)).to.be.bignumber.equal(
await this.token.totalSupply()
await this.token.totalSupply(),
);
});
});
@ -160,13 +158,13 @@ describe('ERC20Snapshot', function () {
expect(await this.token.balanceOfAt(other, this.initialSnapshotId)).to.be.bignumber.equal('0');
expect(await this.token.balanceOfAt(initialHolder, this.secondSnapshotId)).to.be.bignumber.equal(
await this.token.balanceOf(initialHolder)
await this.token.balanceOf(initialHolder),
);
expect(await this.token.balanceOfAt(recipient, this.secondSnapshotId)).to.be.bignumber.equal(
await this.token.balanceOf(recipient)
await this.token.balanceOf(recipient),
);
expect(await this.token.balanceOfAt(other, this.secondSnapshotId)).to.be.bignumber.equal(
await this.token.balanceOf(other)
await this.token.balanceOf(other),
);
});
});
@ -189,13 +187,13 @@ describe('ERC20Snapshot', function () {
for (const id of this.secondSnapshotIds) {
expect(await this.token.balanceOfAt(initialHolder, id)).to.be.bignumber.equal(
await this.token.balanceOf(initialHolder)
await this.token.balanceOf(initialHolder),
);
expect(await this.token.balanceOfAt(recipient, id)).to.be.bignumber.equal(
await this.token.balanceOf(recipient)
await this.token.balanceOf(recipient),
);
expect(await this.token.balanceOfAt(other, id)).to.be.bignumber.equal(
await this.token.balanceOf(other)
await this.token.balanceOf(other),
);
}
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers');
const ERC20ReturnFalseMock = contract.fromArtifact('ERC20ReturnFalseMock');
const ERC20ReturnTrueMock = contract.fromArtifact('ERC20ReturnTrueMock');
const ERC20NoReturnMock = contract.fromArtifact('ERC20NoReturnMock');
const SafeERC20Wrapper = contract.fromArtifact('SafeERC20Wrapper');
const ERC20ReturnFalseMock = artifacts.require('ERC20ReturnFalseMock');
const ERC20ReturnTrueMock = artifacts.require('ERC20ReturnTrueMock');
const ERC20NoReturnMock = artifacts.require('ERC20NoReturnMock');
const SafeERC20Wrapper = artifacts.require('SafeERC20Wrapper');
describe('SafeERC20', function () {
contract('SafeERC20', function (accounts) {
const [ hasNoCode ] = accounts;
describe('with address that has no contract code', function () {
@ -97,7 +95,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when decreasing the allowance', async function () {
await expectRevert(
this.wrapper.decreaseAllowance(10),
'SafeERC20: decreased allowance below zero'
'SafeERC20: decreased allowance below zero',
);
});
});
@ -110,7 +108,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when approving a non-zero allowance', async function () {
await expectRevert(
this.wrapper.approve(20),
'SafeERC20: approve from non-zero to non-zero allowance'
'SafeERC20: approve from non-zero to non-zero allowance',
);
});
@ -129,7 +127,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when decreasing the allowance to a negative value', async function () {
await expectRevert(
this.wrapper.decreaseAllowance(200),
'SafeERC20: decreased allowance below zero'
'SafeERC20: decreased allowance below zero',
);
});
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC20Mock = contract.fromArtifact('ERC20Mock');
const TokenTimelock = contract.fromArtifact('TokenTimelock');
const ERC20Mock = artifacts.require('ERC20Mock');
const TokenTimelock = artifacts.require('TokenTimelock');
describe('TokenTimelock', function () {
contract('TokenTimelock', function (accounts) {
const [ beneficiary ] = accounts;
const name = 'My Token';
@ -24,7 +22,7 @@ describe('TokenTimelock', function () {
const pastReleaseTime = (await time.latest()).sub(time.duration.years(1));
await expectRevert(
TokenTimelock.new(this.token.address, beneficiary, pastReleaseTime),
'TokenTimelock: release time is before current time'
'TokenTimelock: release time is before current time',
);
});

@ -38,7 +38,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await expectRevert(this.token.burn(amount, { from: owner }),
'ERC20: burn amount exceeds balance'
'ERC20: burn amount exceeds balance',
);
});
});
@ -87,7 +87,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await this.token.approve(burner, amount, { from: owner });
await expectRevert(this.token.burnFrom(owner, amount, { from: burner }),
'ERC20: burn amount exceeds balance'
'ERC20: burn amount exceeds balance',
);
});
});
@ -98,7 +98,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () {
await this.token.approve(burner, allowance, { from: owner });
await expectRevert(this.token.burnFrom(owner, allowance.addn(1), { from: burner }),
'ERC20: burn amount exceeds allowance'
'ERC20: burn amount exceeds allowance',
);
});
});

@ -1,5 +1,3 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
@ -7,10 +5,10 @@ const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
const ERC721Mock = contract.fromArtifact('ERC721Mock');
const ERC721ReceiverMock = contract.fromArtifact('ERC721ReceiverMock');
const ERC721Mock = artifacts.require('ERC721Mock');
const ERC721ReceiverMock = artifacts.require('ERC721ReceiverMock');
describe('ERC721', function () {
contract('ERC721', function (accounts) {
const [owner, newOwner, approved, anotherApproved, operator, other] = accounts;
const name = 'Non Fungible Token';
@ -56,7 +54,7 @@ describe('ERC721', function () {
it('reverts when queried for non existent token id', async function () {
await expectRevert(
this.token.tokenURI(nonExistentTokenId), 'ERC721Metadata: URI query for nonexistent token'
this.token.tokenURI(nonExistentTokenId), 'ERC721Metadata: URI query for nonexistent token',
);
});
@ -67,7 +65,7 @@ describe('ERC721', function () {
it('reverts when setting for non existent token id', async function () {
await expectRevert(
this.token.setTokenURI(nonExistentTokenId, sampleUri), 'ERC721Metadata: URI set of nonexistent token'
this.token.setTokenURI(nonExistentTokenId, sampleUri), 'ERC721Metadata: URI set of nonexistent token',
);
});
@ -105,7 +103,7 @@ describe('ERC721', function () {
expect(await this.token.exists(firstTokenId)).to.equal(false);
await expectRevert(
this.token.tokenURI(firstTokenId), 'ERC721Metadata: URI query for nonexistent token'
this.token.tokenURI(firstTokenId), 'ERC721Metadata: URI query for nonexistent token',
);
});
});
@ -134,7 +132,7 @@ describe('ERC721', function () {
context('when querying the zero address', function () {
it('throws', async function () {
await expectRevert(
this.token.balanceOf(ZERO_ADDRESS), 'ERC721: balance query for the zero address'
this.token.balanceOf(ZERO_ADDRESS), 'ERC721: balance query for the zero address',
);
});
});
@ -154,7 +152,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token'
this.token.ownerOf(tokenId), 'ERC721: owner query for nonexistent token',
);
});
});
@ -259,10 +257,10 @@ describe('ERC721', function () {
it('keeps same tokens by index', async function () {
if (!this.token.tokenOfOwnerByIndex) return;
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(owner, i))
[0, 1].map(i => this.token.tokenOfOwnerByIndex(owner, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(
[firstTokenId.toNumber(), secondTokenId.toNumber()]
[firstTokenId.toNumber(), secondTokenId.toNumber()],
);
});
});
@ -271,7 +269,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, other, other, tokenId, { from: owner }),
'ERC721: transfer of token that is not own'
'ERC721: transfer of token that is not own',
);
});
});
@ -280,7 +278,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, tokenId, { from: other }),
'ERC721: transfer caller is not owner nor approved'
'ERC721: transfer caller is not owner nor approved',
);
});
});
@ -289,7 +287,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, other, nonExistentTokenId, { from: owner }),
'ERC721: operator query for nonexistent token'
'ERC721: operator query for nonexistent token',
);
});
});
@ -298,7 +296,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner }),
'ERC721: transfer to the zero address'
'ERC721: transfer to the zero address',
);
});
});
@ -364,7 +362,7 @@ describe('ERC721', function () {
nonExistentTokenId,
{ from: owner },
),
'ERC721: operator query for nonexistent token'
'ERC721: operator query for nonexistent token',
);
});
});
@ -384,7 +382,7 @@ describe('ERC721', function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer'
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
@ -394,7 +392,7 @@ describe('ERC721', function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting'
'ERC721ReceiverMock: reverting',
);
});
});
@ -404,7 +402,7 @@ describe('ERC721', function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeTransferFrom(owner, nonReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer'
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
@ -443,7 +441,7 @@ describe('ERC721', function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
await expectRevert(
this.token.safeMint(invalidReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer'
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
@ -453,7 +451,7 @@ describe('ERC721', function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721ReceiverMock: reverting'
'ERC721ReceiverMock: reverting',
);
});
});
@ -463,7 +461,7 @@ describe('ERC721', function () {
const nonReceiver = this.token;
await expectRevert(
this.token.safeMint(nonReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer'
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
@ -552,7 +550,7 @@ describe('ERC721', function () {
context('when the address that receives the approval is the owner', function () {
it('reverts', async function () {
await expectRevert(
this.token.approve(owner, tokenId, { from: owner }), 'ERC721: approval to current owner'
this.token.approve(owner, tokenId, { from: owner }), 'ERC721: approval to current owner',
);
});
});
@ -674,7 +672,7 @@ describe('ERC721', function () {
it('reverts', async function () {
await expectRevert(
this.token.getApproved(nonExistentTokenId),
'ERC721: approved query for nonexistent token'
'ERC721: approved query for nonexistent token',
);
});
});
@ -682,7 +680,7 @@ describe('ERC721', function () {
context('when token has been minted ', async function () {
it('should return the zero address', async function () {
expect(await this.token.getApproved(firstTokenId)).to.be.equal(
ZERO_ADDRESS
ZERO_ADDRESS,
);
});
@ -714,7 +712,7 @@ describe('ERC721', 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 expectRevert(
this.token.tokenOfOwnerByIndex(owner, 2), 'EnumerableSet: index out of bounds'
this.token.tokenOfOwnerByIndex(owner, 2), 'EnumerableSet: index out of bounds',
);
});
});
@ -722,7 +720,7 @@ describe('ERC721', function () {
describe('when the given address does not own any token', function () {
it('reverts', async function () {
await expectRevert(
this.token.tokenOfOwnerByIndex(other, 0), 'EnumerableSet: index out of bounds'
this.token.tokenOfOwnerByIndex(other, 0), 'EnumerableSet: index out of bounds',
);
});
});
@ -736,7 +734,7 @@ describe('ERC721', function () {
it('returns correct token IDs for target', async function () {
expect(await this.token.balanceOf(other)).to.be.bignumber.equal('2');
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenOfOwnerByIndex(other, i))
[0, 1].map(i => this.token.tokenOfOwnerByIndex(other, i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
@ -745,7 +743,7 @@ describe('ERC721', function () {
it('returns empty collection for original owner', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenOfOwnerByIndex(owner, 0), 'EnumerableSet: index out of bounds'
this.token.tokenOfOwnerByIndex(owner, 0), 'EnumerableSet: index out of bounds',
);
});
});
@ -754,7 +752,7 @@ describe('ERC721', function () {
describe('tokenByIndex', function () {
it('returns all tokens', async function () {
const tokensListed = await Promise.all(
[0, 1].map(i => this.token.tokenByIndex(i))
[0, 1].map(i => this.token.tokenByIndex(i)),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(),
secondTokenId.toNumber()]);
@ -762,7 +760,7 @@ describe('ERC721', function () {
it('reverts if index is greater than supply', async function () {
await expectRevert(
this.token.tokenByIndex(2), 'EnumerableMap: index out of bounds'
this.token.tokenByIndex(2), 'EnumerableMap: index out of bounds',
);
});
@ -778,10 +776,10 @@ describe('ERC721', function () {
expect(await this.token.totalSupply()).to.be.bignumber.equal('3');
const tokensListed = await Promise.all(
[0, 1, 2].map(i => this.token.tokenByIndex(i))
[0, 1, 2].map(i => this.token.tokenByIndex(i)),
);
const expectedTokens = [firstTokenId, secondTokenId, newTokenId, anotherNewTokenId].filter(
x => (x !== tokenId)
x => (x !== tokenId),
);
expect(tokensListed.map(t => t.toNumber())).to.have.members(expectedTokens.map(t => t.toNumber()));
});
@ -792,7 +790,7 @@ describe('ERC721', function () {
describe('_mint(address, uint256)', function () {
it('reverts with a null destination address', async function () {
await expectRevert(
this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address'
this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address',
);
});
@ -827,7 +825,7 @@ describe('ERC721', function () {
describe('_burn', function () {
it('reverts when burning a non-existent token id', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token'
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
@ -853,7 +851,7 @@ describe('ERC721', function () {
it('deletes the token', async function () {
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
await expectRevert(
this.token.ownerOf(firstTokenId), 'ERC721: owner query for nonexistent token'
this.token.ownerOf(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
@ -869,13 +867,13 @@ describe('ERC721', function () {
await this.token.burn(secondTokenId, { from: owner });
expect(await this.token.totalSupply()).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenByIndex(0), 'EnumerableMap: index out of bounds'
this.token.tokenByIndex(0), 'EnumerableMap: index out of bounds',
);
});
it('reverts when burning a token id that has been deleted', async function () {
await expectRevert(
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token'
this.token.burn(firstTokenId), 'ERC721: owner query for nonexistent token',
);
});
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC721BurnableMock = contract.fromArtifact('ERC721BurnableMock');
const ERC721BurnableMock = artifacts.require('ERC721BurnableMock');
describe('ERC721Burnable', function () {
contract('ERC721Burnable', function (accounts) {
const [owner, approved] = accounts;
const firstTokenId = new BN(1);
@ -40,7 +38,7 @@ describe('ERC721Burnable', function () {
it('burns the given token ID and adjusts the balance of the owner', async function () {
await expectRevert(
this.token.ownerOf(tokenId),
'ERC721: owner query for nonexistent token'
'ERC721: owner query for nonexistent token',
);
expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
});
@ -64,7 +62,7 @@ describe('ERC721Burnable', function () {
context('getApproved', function () {
it('reverts', async function () {
await expectRevert(
this.token.getApproved(tokenId), 'ERC721: approved query for nonexistent token'
this.token.getApproved(tokenId), 'ERC721: approved query for nonexistent token',
);
});
});
@ -73,7 +71,7 @@ describe('ERC721Burnable', function () {
describe('when the given token ID was not tracked by this contract', function () {
it('reverts', async function () {
await expectRevert(
this.token.burn(unknownTokenId, { from: owner }), 'ERC721: operator query for nonexistent token'
this.token.burn(unknownTokenId, { from: owner }), 'ERC721: operator query for nonexistent token',
);
});
});

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC721Holder = contract.fromArtifact('ERC721Holder');
const ERC721Mock = contract.fromArtifact('ERC721Mock');
const ERC721Holder = artifacts.require('ERC721Holder');
const ERC721Mock = artifacts.require('ERC721Mock');
describe('ERC721Holder', function () {
contract('ERC721Holder', function (accounts) {
const [ owner ] = accounts;
const name = 'Non Fungible Token';

@ -1,13 +1,11 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC721PausableMock = contract.fromArtifact('ERC721PausableMock');
const ERC721PausableMock = artifacts.require('ERC721PausableMock');
describe('ERC721Pausable', function () {
contract('ERC721Pausable', function (accounts) {
const [ owner, receiver, operator ] = accounts;
const name = 'Non Fungible Token';
@ -31,36 +29,36 @@ describe('ERC721Pausable', function () {
it('reverts when trying to transferFrom', async function () {
await expectRevert(
this.token.transferFrom(owner, receiver, firstTokenId, { from: owner }),
'ERC721Pausable: token transfer while paused'
'ERC721Pausable: token transfer while paused',
);
});
it('reverts when trying to safeTransferFrom', async function () {
await expectRevert(
this.token.safeTransferFrom(owner, receiver, firstTokenId, { from: owner }),
'ERC721Pausable: token transfer while paused'
'ERC721Pausable: token transfer while paused',
);
});
it('reverts when trying to safeTransferFrom with data', async function () {
await expectRevert(
this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](
owner, receiver, firstTokenId, mockData, { from: owner }
), 'ERC721Pausable: token transfer while paused'
owner, receiver, firstTokenId, mockData, { from: owner },
), 'ERC721Pausable: token transfer while paused',
);
});
it('reverts when trying to mint', async function () {
await expectRevert(
this.token.mint(receiver, secondTokenId),
'ERC721Pausable: token transfer while paused'
'ERC721Pausable: token transfer while paused',
);
});
it('reverts when trying to burn', async function () {
await expectRevert(
this.token.burn(firstTokenId),
'ERC721Pausable: token transfer while paused'
'ERC721Pausable: token transfer while paused',
);
});

@ -1,10 +1,9 @@
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const { expect } = require('chai');
const ERC777SenderRecipientMock = contract.fromArtifact('ERC777SenderRecipientMock');
const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock');
function shouldBehaveLikeERC777DirectSendBurn (holder, recipient, data) {
shouldBehaveLikeERC777DirectSend(holder, recipient, data);
@ -58,15 +57,15 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
it('reverts when sending more than the balance', async function () {
const balance = await this.token.balanceOf(holder);
await expectRevert.unspecified(
this.token.operatorSend(holder, recipient, balance.addn(1), data, operatorData, { from: operator })
this.token.operatorSend(holder, recipient, balance.addn(1), data, operatorData, { from: operator }),
);
});
it('reverts when sending to the zero address', async function () {
await expectRevert.unspecified(
this.token.operatorSend(
holder, ZERO_ADDRESS, new BN('1'), data, operatorData, { from: operator }
)
holder, ZERO_ADDRESS, new BN('1'), data, operatorData, { from: operator },
),
);
});
});
@ -78,7 +77,7 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
it('reverts when sending a non-zero amount', async function () {
await expectRevert.unspecified(
this.token.operatorSend(holder, recipient, new BN('1'), data, operatorData, { from: operator })
this.token.operatorSend(holder, recipient, new BN('1'), data, operatorData, { from: operator }),
);
});
@ -86,8 +85,8 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
// This is not yet reflected in the spec
await expectRevert.unspecified(
this.token.operatorSend(
ZERO_ADDRESS, recipient, new BN('0'), data, operatorData, { from: operator }
)
ZERO_ADDRESS, recipient, new BN('0'), data, operatorData, { from: operator },
),
);
});
});
@ -135,7 +134,7 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
it('reverts when burning more than the balance', async function () {
const balance = await this.token.balanceOf(holder);
await expectRevert.unspecified(
this.token.operatorBurn(holder, balance.addn(1), data, operatorData, { from: operator })
this.token.operatorBurn(holder, balance.addn(1), data, operatorData, { from: operator }),
);
});
});
@ -147,7 +146,7 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
it('reverts when burning a non-zero amount', async function () {
await expectRevert.unspecified(
this.token.operatorBurn(holder, new BN('1'), data, operatorData, { from: operator })
this.token.operatorBurn(holder, new BN('1'), data, operatorData, { from: operator }),
);
});
@ -155,8 +154,8 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
// This is not yet reflected in the spec
await expectRevert.unspecified(
this.token.operatorBurn(
ZERO_ADDRESS, new BN('0'), data, operatorData, { from: operator }
)
ZERO_ADDRESS, new BN('0'), data, operatorData, { from: operator },
),
);
});
});
@ -282,7 +281,7 @@ function shouldBehaveLikeERC777InternalMint (recipient, operator, amount, data,
it('reverts when minting tokens for the zero address', async function () {
await expectRevert.unspecified(
this.token.mintInternal(ZERO_ADDRESS, amount, data, operatorData, { from: operator })
this.token.mintInternal(ZERO_ADDRESS, amount, data, operatorData, { from: operator }),
);
});
}
@ -328,13 +327,13 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am
it('operatorSend reverts', async function () {
await expectRevert.unspecified(
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator })
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }),
);
});
it('mint (internal) reverts', async function () {
await expectRevert.unspecified(
this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator })
this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator }),
);
});
});
@ -389,7 +388,7 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am
it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () {
const { tx } = await this.token.mintInternal(
this.recipient, amount, data, operatorData, { from: operator }
this.recipient, amount, data, operatorData, { from: operator },
);
const postRecipientBalance = await this.token.balanceOf(this.recipient);
@ -422,7 +421,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
it('operatorSend reverts', async function () {
await expectRevert.unspecified(
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator })
this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }),
);
});
@ -432,7 +431,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
it('operatorBurn reverts', async function () {
await expectRevert.unspecified(
this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator })
this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }),
);
});
});
@ -491,7 +490,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender });
await assertTokensToSendCalled(
this.token, tx, this.sender, this.sender, ZERO_ADDRESS, amount, data, null, preSenderBalance
this.token, tx, this.sender, this.sender, ZERO_ADDRESS, amount, data, null, preSenderBalance,
);
});
@ -501,7 +500,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator });
await assertTokensToSendCalled(
this.token, tx, operator, this.sender, ZERO_ADDRESS, amount, data, operatorData, preSenderBalance
this.token, tx, operator, this.sender, ZERO_ADDRESS, amount, data, operatorData, preSenderBalance,
);
});
});

@ -1,5 +1,3 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert, singletons } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
@ -19,10 +17,10 @@ const {
shouldBehaveLikeERC20Approve,
} = require('../ERC20/ERC20.behavior');
const ERC777 = contract.fromArtifact('ERC777Mock');
const ERC777SenderRecipientMock = contract.fromArtifact('ERC777SenderRecipientMock');
const ERC777 = artifacts.require('ERC777Mock');
const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock');
describe('ERC777', function () {
contract('ERC777', function (accounts) {
const [ registryFunder, holder, defaultOperatorA, defaultOperatorB, newOperator, anyone ] = accounts;
const initialSupply = new BN('10000');
@ -53,7 +51,7 @@ describe('ERC777', function () {
describe('when the owner is the zero address', function () {
it('reverts', async function () {
await expectRevert(this.token.approveInternal(ZERO_ADDRESS, anyone, initialSupply),
'ERC777: approve from the zero address'
'ERC777: approve from the zero address',
);
});
});
@ -182,13 +180,13 @@ describe('ERC777', function () {
it('reverts when self-authorizing', async function () {
await expectRevert(
this.token.authorizeOperator(holder, { from: holder }), 'ERC777: authorizing self as operator'
this.token.authorizeOperator(holder, { from: holder }), 'ERC777: authorizing self as operator',
);
});
it('reverts when self-revoking', async function () {
await expectRevert(
this.token.revokeOperator(holder, { from: holder }), 'ERC777: revoking self as operator'
this.token.revokeOperator(holder, { from: holder }), 'ERC777: revoking self as operator',
);
});
@ -252,7 +250,7 @@ describe('ERC777', function () {
it('cannot be revoked for themselves', async function () {
await expectRevert(
this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA }),
'ERC777: revoking self as operator'
'ERC777: revoking self as operator',
);
});

@ -1,13 +1,11 @@
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { balance, ether, expectRevert, send, expectEvent } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const AddressImpl = contract.fromArtifact('AddressImpl');
const EtherReceiver = contract.fromArtifact('EtherReceiverMock');
const CallReceiverMock = contract.fromArtifact('CallReceiverMock');
const AddressImpl = artifacts.require('AddressImpl');
const EtherReceiver = artifacts.require('EtherReceiverMock');
const CallReceiverMock = artifacts.require('CallReceiverMock');
describe('Address', function () {
contract('Address', function (accounts) {
const [ recipient, other ] = accounts;
beforeEach(async function () {
@ -85,7 +83,7 @@ describe('Address', function () {
await this.contractRecipient.setAcceptEther(false);
await expectRevert(
this.mock.sendValue(this.contractRecipient.address, funds),
'Address: unable to send value, recipient may have reverted'
'Address: unable to send value, recipient may have reverted',
);
});
});
@ -120,7 +118,7 @@ describe('Address', function () {
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed'
'Address: low-level call failed',
);
});
@ -133,7 +131,7 @@ describe('Address', function () {
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'CallReceiverMock: reverting'
'CallReceiverMock: reverting',
);
});
@ -145,10 +143,10 @@ describe('Address', function () {
}, []);
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed'
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '90000' }),
'Address: low-level call failed',
);
}).timeout(5000);
});
it('reverts when the called function throws', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
@ -159,7 +157,7 @@ describe('Address', function () {
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed'
'Address: low-level call failed',
);
});
@ -172,7 +170,7 @@ describe('Address', function () {
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed'
'Address: low-level call failed',
);
});
});
@ -222,7 +220,7 @@ describe('Address', function () {
await expectRevert(
this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount),
'Address: insufficient balance for call'
'Address: insufficient balance for call',
);
});
@ -255,7 +253,7 @@ describe('Address', function () {
expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0');
const receipt = await this.mock.functionCallWithValue(
this.contractRecipient.address, abiEncodedCall, amount, { from: other, value: amount }
this.contractRecipient.address, abiEncodedCall, amount, { from: other, value: amount },
);
expect(await tracker.delta()).to.be.bignumber.equal(amount);
@ -274,9 +272,112 @@ describe('Address', function () {
await send.ether(other, this.mock.address, amount);
await expectRevert(
this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount),
'Address: low-level call with value failed'
'Address: low-level call with value failed',
);
});
});
});
describe('functionStaticCall', function () {
beforeEach(async function () {
this.contractRecipient = await CallReceiverMock.new();
});
it('calls the requested function', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockStaticFunction',
type: 'function',
inputs: [],
}, []);
const receipt = await this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall);
expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
});
it('reverts on a non-static function', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunction',
type: 'function',
inputs: [],
}, []);
await expectRevert(
this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level static call failed',
);
});
it('bubbles up revert reason', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunctionRevertsReason',
type: 'function',
inputs: [],
}, []);
await expectRevert(
this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall),
'CallReceiverMock: reverting',
);
});
it('reverts when address is not a contract', async function () {
const [ recipient ] = accounts;
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunction',
type: 'function',
inputs: [],
}, []);
await expectRevert(
this.mock.functionStaticCall(recipient, abiEncodedCall),
'Address: static call to non-contract',
);
});
});
describe('functionDelegateCall', function () {
beforeEach(async function () {
this.contractRecipient = await CallReceiverMock.new();
});
it('delegate calls the requested function', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunctionWritesStorage',
type: 'function',
inputs: [],
}, []);
const receipt = await this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall);
expectEvent(receipt, 'CallReturnValue', { data: '0x1234' });
expect(await this.mock.sharedAnswer()).to.equal('42');
});
it('bubbles up revert reason', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunctionRevertsReason',
type: 'function',
inputs: [],
}, []);
await expectRevert(
this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall),
'CallReceiverMock: reverting',
);
});
it('reverts when address is not a contract', async function () {
const [ recipient ] = accounts;
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
name: 'mockFunction',
type: 'function',
inputs: [],
}, []);
await expectRevert(
this.mock.functionDelegateCall(recipient, abiEncodedCall),
'Address: delegate call to non-contract',
);
});
});
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save