Commit 4972bf4f by Francisco Giordano

Merge branch 'master' into solc-0.7

parents a1408a34 3d2e5c03
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 @@ ...@@ -18,6 +18,8 @@
"contract": false, "contract": false,
"assert": false, "assert": false,
"web3": false, "web3": false,
"usePlugin": false,
"extendEnvironment": false,
}, },
"rules": { "rules": {
......
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
...@@ -50,3 +50,10 @@ contracts/README.md ...@@ -50,3 +50,10 @@ contracts/README.md
# temporary artifact from solidity-coverage # temporary artifact from solidity-coverage
allFiredEvents allFiredEvents
.coverage_artifacts
.coverage_cache
.coverage_contracts
# buidler
cache
artifacts
...@@ -4,5 +4,8 @@ module.exports = { ...@@ -4,5 +4,8 @@ module.exports = {
compileCommand: 'npm run compile', compileCommand: 'npm run compile',
skipFiles: [ skipFiles: [
'mocks', 'mocks',
] ],
providerOptions: {
default_balance_ether: '10000000000000000000000000',
},
} }
# Changelog # 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) ## 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)) * 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) ## 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) ## 3.2.0 (2020-09-10)
......
...@@ -44,13 +44,6 @@ npm test ...@@ -44,13 +44,6 @@ npm test
npm run lint 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. 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 *IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
[![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](https://docs.openzeppelin.com/contracts) [![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) [![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) [![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. **A library for secure smart contract development.** Build on a solid foundation of community-vetted code.
...@@ -11,7 +10,7 @@ ...@@ -11,7 +10,7 @@
* Flexible [role-based permissioning](https://docs.openzeppelin.com/contracts/access-control) scheme. * 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. * 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! * 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 ## Overview
......
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',
},
};
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/ ...@@ -5,8 +5,85 @@ NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/
Contract modules for authorization and access control mechanisms. Contract modules for authorization and access control mechanisms.
== Contracts == Authorization
{{Ownable}} {{Ownable}}
{{AccessControl}} {{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.
...@@ -52,13 +52,8 @@ library ECDSA { ...@@ -52,13 +52,8 @@ library ECDSA {
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // 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 // 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. // these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
revert("ECDSA: invalid signature 's' value"); require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
}
if (v != 27 && v != 28) {
revert("ECDSA: invalid signature 'v' value");
}
// If the signature is valid (and not malleable), return the signer address // If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s); address signer = ecrecover(hash, v, r, s);
......
...@@ -5,6 +5,8 @@ pragma solidity ^0.7.0; ...@@ -5,6 +5,8 @@ pragma solidity ^0.7.0;
import "../utils/Address.sol"; import "../utils/Address.sol";
contract AddressImpl { contract AddressImpl {
string public sharedAnswer;
event CallReturnValue(string data); event CallReturnValue(string data);
function isContract(address account) external view returns (bool) { function isContract(address account) external view returns (bool) {
...@@ -17,13 +19,21 @@ contract AddressImpl { ...@@ -17,13 +19,21 @@ contract AddressImpl {
function functionCall(address target, bytes calldata data) external { function functionCall(address target, bytes calldata data) external {
bytes memory returnData = Address.functionCall(target, data); bytes memory returnData = Address.functionCall(target, data);
emit CallReturnValue(abi.decode(returnData, (string))); emit CallReturnValue(abi.decode(returnData, (string)));
} }
function functionCallWithValue(address target, bytes calldata data, uint256 value) external payable { function functionCallWithValue(address target, bytes calldata data, uint256 value) external payable {
bytes memory returnData = Address.functionCallWithValue(target, data, value); 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))); emit CallReturnValue(abi.decode(returnData, (string)));
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
contract CallReceiverMock { contract CallReceiverMock {
string public sharedAnswer;
event MockFunctionCalled(); event MockFunctionCalled();
...@@ -20,6 +21,10 @@ contract CallReceiverMock { ...@@ -20,6 +21,10 @@ contract CallReceiverMock {
return "0x1234"; return "0x1234";
} }
function mockStaticFunction() public pure returns (string memory) {
return "0x1234";
}
function mockFunctionRevertsNoReason() public payable { function mockFunctionRevertsNoReason() public payable {
revert(); revert();
} }
...@@ -37,4 +42,9 @@ contract CallReceiverMock { ...@@ -37,4 +42,9 @@ contract CallReceiverMock {
_array.push(i); _array.push(i);
} }
} }
function mockFunctionWritesStorage() public returns (string memory) {
sharedAnswer = "42";
return "0x1234";
}
} }
...@@ -23,5 +23,5 @@ contract Create2Impl { ...@@ -23,5 +23,5 @@ contract Create2Impl {
return Create2.computeAddress(salt, codeHash, deployer); return Create2.computeAddress(salt, codeHash, deployer);
} }
receive() payable external {} receive() external payable {}
} }
...@@ -15,7 +15,7 @@ contract DummyImplementation { ...@@ -15,7 +15,7 @@ contract DummyImplementation {
value = 10; value = 10;
} }
function initializePayable() payable public { function initializePayable() public payable {
value = 100; value = 100;
} }
...@@ -23,7 +23,7 @@ contract DummyImplementation { ...@@ -23,7 +23,7 @@ contract DummyImplementation {
value = _value; value = _value;
} }
function initializePayable(uint256 _value) payable public { function initializePayable(uint256 _value) public payable {
value = _value; value = _value;
} }
...@@ -47,7 +47,7 @@ contract DummyImplementation { ...@@ -47,7 +47,7 @@ contract DummyImplementation {
} }
contract DummyImplementationV2 is DummyImplementation { contract DummyImplementationV2 is DummyImplementation {
function migrate(uint256 newVal) payable public { function migrate(uint256 newVal) public payable {
value = newVal; value = newVal;
} }
......
...@@ -4,6 +4,37 @@ pragma solidity ^0.7.0; ...@@ -4,6 +4,37 @@ pragma solidity ^0.7.0;
import "../utils/EnumerableSet.sol"; 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 // AddressSet
contract EnumerableAddressSetMock { contract EnumerableAddressSetMock {
using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.AddressSet;
......
{ {
"name": "@openzeppelin/contracts", "name": "@openzeppelin/contracts",
"version": "3.2.2-solc-0.7",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"version": "3.2.2-solc-0.7",
"files": [ "files": [
"**/*.sol", "**/*.sol",
"/build/contracts/*.json", "/build/contracts/*.json",
......
...@@ -60,7 +60,7 @@ abstract contract Proxy { ...@@ -60,7 +60,7 @@ abstract contract Proxy {
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * @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. * function in the contract matches the call data.
*/ */
fallback () payable external { fallback () external payable {
_fallback(); _fallback();
} }
...@@ -68,7 +68,7 @@ abstract contract Proxy { ...@@ -68,7 +68,7 @@ abstract contract Proxy {
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty. * is empty.
*/ */
receive () payable external { receive () external payable {
_fallback(); _fallback();
} }
......
...@@ -23,7 +23,7 @@ import "./UpgradeableProxy.sol"; ...@@ -23,7 +23,7 @@ import "./UpgradeableProxy.sol";
* to sudden errors when trying to call a function from the proxy implementation. * 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, * 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 { contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
......
...@@ -5,7 +5,6 @@ pragma solidity ^0.7.0; ...@@ -5,7 +5,6 @@ pragma solidity ^0.7.0;
import "../../GSN/Context.sol"; import "../../GSN/Context.sol";
import "./IERC20.sol"; import "./IERC20.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/** /**
* @dev Implementation of the {IERC20} interface. * @dev Implementation of the {IERC20} interface.
...@@ -33,7 +32,6 @@ import "../../utils/Address.sol"; ...@@ -33,7 +32,6 @@ import "../../utils/Address.sol";
*/ */
contract ERC20 is Context, IERC20 { contract ERC20 is Context, IERC20 {
using SafeMath for uint256; using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances; mapping (address => uint256) private _balances;
...@@ -142,9 +140,10 @@ contract ERC20 is Context, IERC20 { ...@@ -142,9 +140,10 @@ contract ERC20 is Context, IERC20 {
* @dev See {IERC20-transferFrom}. * @dev See {IERC20-transferFrom}.
* *
* Emits an {Approval} event indicating the updated allowance. This is not * 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: * Requirements:
*
* - `sender` and `recipient` cannot be the zero address. * - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`. * - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least * - the caller must have allowance for ``sender``'s tokens of at least
...@@ -222,7 +221,7 @@ contract ERC20 is Context, IERC20 { ...@@ -222,7 +221,7 @@ contract ERC20 is Context, IERC20 {
* *
* Emits a {Transfer} event with `from` set to the zero address. * Emits a {Transfer} event with `from` set to the zero address.
* *
* Requirements * Requirements:
* *
* - `to` cannot be the zero address. * - `to` cannot be the zero address.
*/ */
...@@ -242,7 +241,7 @@ contract ERC20 is Context, IERC20 { ...@@ -242,7 +241,7 @@ contract ERC20 is Context, IERC20 {
* *
* Emits a {Transfer} event with `to` set to the zero address. * Emits a {Transfer} event with `to` set to the zero address.
* *
* Requirements * Requirements:
* *
* - `account` cannot be the zero address. * - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens. * - `account` must have at least `amount` tokens.
......
...@@ -17,6 +17,5 @@ interface IERC721Receiver { ...@@ -17,6 +17,5 @@ interface IERC721Receiver {
* *
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/ */
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
external returns (bytes4);
} }
...@@ -24,14 +24,14 @@ library Address { ...@@ -24,14 +24,14 @@ library Address {
* ==== * ====
*/ */
function isContract(address account) internal view returns (bool) { function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts // This method relies on extcodesize, which returns 0 for contracts in
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // construction, since the code is only stored at the end of the
// for accounts without code, i.e. `keccak256('')` // constructor execution.
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; uint256 size;
// solhint-disable-next-line no-inline-assembly // solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) } assembly { size := extcodesize(account) }
return (codehash != accountHash && codehash != 0x0); return size > 0;
} }
/** /**
...@@ -87,7 +87,7 @@ library Address { ...@@ -87,7 +87,7 @@ library Address {
* _Available since v3.1._ * _Available since v3.1._
*/ */
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 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 { ...@@ -113,14 +113,62 @@ library Address {
*/ */
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 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"); 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 // 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) { if (success) {
return returndata; return returndata;
} else { } else {
......
...@@ -23,8 +23,8 @@ pragma solidity ^0.7.0; ...@@ -23,8 +23,8 @@ pragma solidity ^0.7.0;
* } * }
* ``` * ```
* *
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256` * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* (`UintSet`) are supported. * and `uint256` (`UintSet`) are supported.
*/ */
library EnumerableSet { library EnumerableSet {
// To implement this library for multiple types with as little code // To implement this library for multiple types with as little code
...@@ -132,6 +132,60 @@ library EnumerableSet { ...@@ -132,6 +132,60 @@ library EnumerableSet {
return set._values[index]; 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 // AddressSet
struct AddressSet { struct AddressSet {
......
* xref:index.adoc[Overview] * 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] * xref:access-control.adoc[Access Control]
...@@ -12,8 +16,4 @@ ...@@ -12,8 +16,4 @@
* xref:gsn.adoc[Gas Station Network] * xref:gsn.adoc[Gas Station Network]
** xref:gsn-strategies.adoc[Strategies] ** xref:gsn-strategies.adoc[Strategies]
* xref:extending-contracts.adoc[Extending Contracts]
* xref:utilities.adoc[Utilities] * xref:utilities.adoc[Utilities]
* xref:releases-stability.adoc[Releases & Stability]
...@@ -185,3 +185,37 @@ for (let i = 0; i < minterCount; ++i) { ...@@ -185,3 +185,37 @@ for (let i = 0; i < minterCount; ++i) {
members.push(await myToken.getRoleMember(MINTER_ROLE, 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 { ...@@ -45,7 +45,7 @@ contract GameItems is ERC1155 {
uint256 public constant SWORD = 3; uint256 public constant SWORD = 3;
uint256 public constant SHIELD = 4; 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, GOLD, 10**18, "");
_mint(msg.sender, SILVER, 10**27, ""); _mint(msg.sender, SILVER, 10**27, "");
_mint(msg.sender, THORS_HAMMER, 1, ""); _mint(msg.sender, THORS_HAMMER, 1, "");
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Flexible xref:access-control.adoc[role-based permissioning] scheme. * Flexible xref:access-control.adoc[role-based permissioning] scheme.
* Reusable xref:utilities.adoc[Solidity components] to build custom contracts and complex decentralized systems. * 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! * 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 == Overview
......
= 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).
{ {
"name": "openzeppelin-solidity", "name": "openzeppelin-solidity",
"version": "3.2.2-solc-0.7",
"description": "Secure Smart Contract library for Solidity", "description": "Secure Smart Contract library for Solidity",
"version": "3.2.2-solc-0.7",
"files": [ "files": [
"/contracts/**/*.sol", "/contracts/**/*.sol",
"/build/contracts/*.json", "/build/contracts/*.json",
"!/contracts/mocks", "!/contracts/mocks",
"!/contracts/examples",
"/test/behaviors" "/test/behaviors"
], ],
"scripts": { "scripts": {
"compile": "scripts/compile.sh", "compile": "buidler compile",
"coverage": "scripts/coverage.sh", "coverage": "buidler coverage",
"docs": "oz-docs", "docs": "oz-docs",
"docs:watch": "npm run docs watch contracts 'docs/*.hbs'", "docs:watch": "npm run docs watch contracts 'docs/*.hbs'",
"prepare-docs": "scripts/prepare-docs.sh", "prepare-docs": "scripts/prepare-docs.sh",
...@@ -20,12 +19,12 @@ ...@@ -20,12 +19,12 @@
"lint:js": "eslint --ignore-path .gitignore .", "lint:js": "eslint --ignore-path .gitignore .",
"lint:js:fix": "eslint --ignore-path .gitignore . --fix", "lint:js:fix": "eslint --ignore-path .gitignore . --fix",
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"", "lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
"prepublish": "rimraf build contracts/build", "prepublish": "rimraf build contracts/build artifacts cache",
"prepare": "npm run compile", "prepare": "npm run compile",
"prepack": "node scripts/remove-ignored-artifacts.js", "prepack": "scripts/prepack.sh",
"release": "scripts/release/release.sh", "release": "scripts/release/release.sh",
"version": "scripts/release/version.sh", "version": "scripts/release/version.sh",
"test": "mocha --exit --recursive" "test": "buidler test"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -46,12 +45,13 @@ ...@@ -46,12 +45,13 @@
}, },
"homepage": "https://openzeppelin.com/contracts/", "homepage": "https://openzeppelin.com/contracts/",
"devDependencies": { "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/docs-utils": "^0.1.0",
"@openzeppelin/gsn-helpers": "^0.2.3", "@openzeppelin/gsn-helpers": "^0.2.3",
"@openzeppelin/gsn-provider": "^0.1.10", "@openzeppelin/gsn-provider": "^0.1.10",
"@openzeppelin/test-environment": "^0.1.4", "@openzeppelin/test-helpers": "^0.5.9",
"@openzeppelin/test-helpers": "^0.5.6",
"chai": "^4.2.0", "chai": "^4.2.0",
"eslint": "^6.5.1", "eslint": "^6.5.1",
"eslint-config-standard": "^14.1.1", "eslint-config-standard": "^14.1.1",
...@@ -60,15 +60,14 @@ ...@@ -60,15 +60,14 @@
"eslint-plugin-node": "^10.0.0", "eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1", "eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1", "eslint-plugin-standard": "^4.0.1",
"ethereumjs-util": "^7.0.1", "ethereumjs-util": "^7.0.7",
"ganache-core-coverage": "https://github.com/OpenZeppelin/ganache-core-coverage/releases/download/2.5.3-coverage/ganache-core-coverage-2.5.3.tgz",
"lodash.startcase": "^4.4.0", "lodash.startcase": "^4.4.0",
"lodash.zip": "^4.2.0", "lodash.zip": "^4.2.0",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"mocha": "^8.0.1", "mocha": "^8.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"solhint": "^3.2.0", "solhint": "^3.2.0",
"solidity-coverage": "github:rotcivegaf/solidity-coverage#5875f5b7bc74d447f3312c9c0e9fc7814b482477", "solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3" "solidity-docgen": "^0.5.3"
}, },
"dependencies": {} "dependencies": {}
......
{ {
"rangeStrategy": "update-lockfile",
"extends": [ "extends": [
"config:base", "github>OpenZeppelin/code-style"
"group:allNonMajor",
"schedule:weekly"
], ],
"packageRules": [ "packageRules": [
{ {
......
#!/usr/bin/env sh
if [ "$SOLC_NIGHTLY" = true ]; then
docker pull ethereum/solc:nightly
fi
export OPENZEPPELIN_NON_INTERACTIVE=true
npx oz compile
#!/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'); ...@@ -7,7 +7,7 @@ const startCase = require('lodash.startcase');
const baseDir = process.argv[2]; const baseDir = process.argv[2];
const files = proc.execFileSync( const files = proc.execFileSync(
'find', [baseDir, '-type', 'f'], { encoding: 'utf8' } 'find', [baseDir, '-type', 'f'], { encoding: 'utf8' },
).split('\n').filter(s => s !== ''); ).split('\n').filter(s => s !== '');
console.log('.API'); console.log('.API');
......
#!/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
File mode changed from 100644 to 100755
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 ...@@ -9,5 +9,12 @@ if [ ! -d node_modules ]; then
fi fi
rm -rf "$OUTDIR" 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" node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"
...@@ -24,7 +24,7 @@ const { version } = require('../../package.json'); ...@@ -24,7 +24,7 @@ const { version } = require('../../package.json');
fs.writeFileSync('CHANGELOG.md', changelog.replace( fs.writeFileSync('CHANGELOG.md', changelog.replace(
unreleased, unreleased,
`## ${version} (${new Date().toISOString().split('T')[0]})`) `## ${version} (${new Date().toISOString().split('T')[0]})`),
); );
cp.execSync('git add CHANGELOG.md', { stdio: 'inherit' }); cp.execSync('git add CHANGELOG.md', { stdio: 'inherit' });
...@@ -23,21 +23,20 @@ const ignorePatternsSubtrees = ignorePatterns ...@@ -23,21 +23,20 @@ const ignorePatternsSubtrees = ignorePatterns
.concat(ignorePatterns.map(pat => path.join(pat, '**/*'))) .concat(ignorePatterns.map(pat => path.join(pat, '**/*')))
.map(p => p.replace(/^\//, '')); .map(p => p.replace(/^\//, ''));
const solcOutput = readJSON('cache/solc-output.json');
const artifactsDir = 'build/contracts'; const artifactsDir = 'build/contracts';
let n = 0; let n = 0;
for (const artifact of fs.readdirSync(artifactsDir)) { for (const sourcePath in solcOutput.contracts) {
const fullArtifactPath = path.join(artifactsDir, artifact);
const { sourcePath: fullSourcePath } = readJSON(fullArtifactPath);
const sourcePath = path.relative('.', fullSourcePath);
const ignore = match.any(sourcePath, ignorePatternsSubtrees); const ignore = match.any(sourcePath, ignorePatternsSubtrees);
if (ignore) { if (ignore) {
fs.unlinkSync(fullArtifactPath); for (const contract in solcOutput.contracts[sourcePath]) {
fs.unlinkSync(path.join(artifactsDir, contract + '.json'));
n += 1; n += 1;
} }
}
} }
console.error(`Removed ${n} mock artifacts`); console.error(`Removed ${n} mock artifacts`);
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],
});
},
};
const { contract } = require('@openzeppelin/test-environment');
const { BN, expectEvent } = require('@openzeppelin/test-helpers'); const { BN, expectEvent } = require('@openzeppelin/test-helpers');
const ContextMock = contract.fromArtifact('ContextMock'); const ContextMock = artifacts.require('ContextMock');
function shouldBehaveLikeRegularContext (sender) { function shouldBehaveLikeRegularContext (sender) {
describe('msgSender', function () { describe('msgSender', function () {
......
const { accounts, contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers'); require('@openzeppelin/test-helpers');
const ContextMock = contract.fromArtifact('ContextMock'); const ContextMock = artifacts.require('ContextMock');
const ContextMockCaller = contract.fromArtifact('ContextMockCaller'); const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('./Context.behavior'); const { shouldBehaveLikeRegularContext } = require('./Context.behavior');
describe('Context', function () { contract('Context', function (accounts) {
const [ sender ] = accounts; const [ sender ] = accounts;
beforeEach(async function () { beforeEach(async function () {
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { constants, expectEvent } = require('@openzeppelin/test-helpers'); const { constants, expectEvent } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers'); const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign'); const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3'); 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 [ signer, sender ] = accounts;
const name = 'Non Fungible Token'; const name = 'Non Fungible Token';
const symbol = 'NFT'; const symbol = 'NFT';
const tokenId = '42'; const tokenId = '42';
before(function () {
setGSNProvider(ERC721GSNRecipientMock, accounts);
});
beforeEach(async function () { beforeEach(async function () {
this.token = await ERC721GSNRecipientMock.new(name, symbol, signer); this.token = await ERC721GSNRecipientMock.new(name, symbol, signer);
}); });
...@@ -41,9 +44,9 @@ describe('ERC721GSNRecipient (integration)', function () { ...@@ -41,9 +44,9 @@ describe('ERC721GSNRecipient (integration)', function () {
await web3.eth.sign( await web3.eth.sign(
web3.utils.soliditySha3( web3.utils.soliditySha3(
// eslint-disable-next-line max-len // 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 data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, this.token.address,
), signer ), signer,
) ),
); );
await testMintToken(this.token, sender, tokenId, { useGSN: true, approveFunction }); await testMintToken(this.token, sender, tokenId, { useGSN: true, approveFunction });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { balance, BN, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { balance, BN, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers'); const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai'); const { expect } = require('chai');
const GSNRecipientMock = contract.fromArtifact('GSNRecipientMock'); const GSNRecipientMock = artifacts.require('GSNRecipientMock');
const ContextMockCaller = contract.fromArtifact('ContextMockCaller'); const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('./Context.behavior'); const { shouldBehaveLikeRegularContext } = require('./Context.behavior');
describe('GSNRecipient', function () { contract('GSNRecipient', function (accounts) {
const [ payee, sender, newRelayHub ] = accounts; const [ payee, sender, newRelayHub ] = accounts;
before(function () {
setGSNProvider(GSNRecipientMock, accounts);
setGSNProvider(ContextMockCaller, accounts);
});
beforeEach(async function () { beforeEach(async function () {
this.recipient = await GSNRecipientMock.new(); this.recipient = await GSNRecipientMock.new();
}); });
...@@ -38,13 +42,13 @@ describe('GSNRecipient', function () { ...@@ -38,13 +42,13 @@ describe('GSNRecipient', function () {
it('cannot upgrade to the same RelayHub', async function () { it('cannot upgrade to the same RelayHub', async function () {
await expectRevert( await expectRevert(
this.recipient.upgradeRelayHub(singletonRelayHub), 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 () { it('cannot upgrade to the zero address', async function () {
await expectRevert( 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',
); );
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { ether, expectEvent } = require('@openzeppelin/test-helpers'); const { ether, expectEvent } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers'); const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai'); const { expect } = require('chai');
const GSNRecipientERC20FeeMock = contract.fromArtifact('GSNRecipientERC20FeeMock'); const GSNRecipientERC20FeeMock = artifacts.require('GSNRecipientERC20FeeMock');
const ERC20 = contract.fromArtifact('ERC20'); const ERC20 = artifacts.require('ERC20');
const IRelayHub = contract.fromArtifact('IRelayHub'); const IRelayHub = artifacts.require('IRelayHub');
describe('GSNRecipientERC20Fee', function () { contract('GSNRecipientERC20Fee', function (accounts) {
const [ sender ] = accounts; const [ sender ] = accounts;
const name = 'FeeToken'; const name = 'FeeToken';
const symbol = 'FTKN'; const symbol = 'FTKN';
before(function () {
setGSNProvider(GSNRecipientERC20FeeMock, accounts);
setGSNProvider(ERC20, accounts);
setGSNProvider(IRelayHub, accounts);
});
beforeEach(async function () { beforeEach(async function () {
this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol); this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol);
this.token = await ERC20.at(await this.recipient.token()); this.token = await ERC20.at(await this.recipient.token());
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectEvent, expectRevert, constants } = require('@openzeppelin/test-helpers'); const { expectEvent, expectRevert, constants } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers'); const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign'); const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3'); const { utils: { toBN } } = require('web3');
const { ZERO_ADDRESS } = constants; 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; const [ signer, other ] = accounts;
before(function () {
setGSNProvider(GSNRecipientSignatureMock, accounts);
});
beforeEach(async function () { beforeEach(async function () {
this.recipient = await GSNRecipientSignatureMock.new(signer); this.recipient = await GSNRecipientSignatureMock.new(signer);
}); });
...@@ -26,9 +29,9 @@ describe('GSNRecipientSignature', function () { ...@@ -26,9 +29,9 @@ describe('GSNRecipientSignature', function () {
it('fails when constructor called with a zero address', async function () { it('fails when constructor called with a zero address', async function () {
await expectRevert( await expectRevert(
GSNRecipientSignatureMock.new( 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 () { ...@@ -49,9 +52,9 @@ describe('GSNRecipientSignature', function () {
web3.utils.soliditySha3( web3.utils.soliditySha3(
// the nonce is not signed // the nonce is not signed
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas) data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas),
), signer ), signer,
) ),
); );
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction })); await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));
...@@ -63,9 +66,9 @@ describe('GSNRecipientSignature', function () { ...@@ -63,9 +66,9 @@ describe('GSNRecipientSignature', function () {
await web3.eth.sign( await web3.eth.sign(
web3.utils.soliditySha3( web3.utils.soliditySha3(
// eslint-disable-next-line max-len // 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 data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), signer ), signer,
) ),
); );
const { tx } = await this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }); const { tx } = await this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction });
...@@ -79,9 +82,9 @@ describe('GSNRecipientSignature', function () { ...@@ -79,9 +82,9 @@ describe('GSNRecipientSignature', function () {
await web3.eth.sign( await web3.eth.sign(
web3.utils.soliditySha3( web3.utils.soliditySha3(
// eslint-disable-next-line max-len // 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 data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), other ), other,
) ),
); );
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction })); await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));
......
## Testing ## 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.
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 [ admin, authorized, otherAuthorized, other, otherAdmin ] = accounts;
const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
...@@ -42,7 +40,7 @@ describe('AccessControl', function () { ...@@ -42,7 +40,7 @@ describe('AccessControl', function () {
it('non-admin cannot grant role to other accounts', async function () { it('non-admin cannot grant role to other accounts', async function () {
await expectRevert( await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: other }), 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 () { ...@@ -76,7 +74,7 @@ describe('AccessControl', function () {
it('non-admin cannot revoke role', async function () { it('non-admin cannot revoke role', async function () {
await expectRevert( await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: other }), 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 () { ...@@ -110,7 +108,7 @@ describe('AccessControl', function () {
it('only the sender can renounce their roles', async function () { it('only the sender can renounce their roles', async function () {
await expectRevert( await expectRevert(
this.accessControl.renounceRole(ROLE, authorized, { from: admin }), 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 () { ...@@ -170,14 +168,14 @@ describe('AccessControl', function () {
it('a role\'s previous admins no longer grant roles', async function () { it('a role\'s previous admins no longer grant roles', async function () {
await expectRevert( await expectRevert(
this.accessControl.grantRole(ROLE, authorized, { from: admin }), 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 () { it('a role\'s previous admins no longer revoke roles', async function () {
await expectRevert( await expectRevert(
this.accessControl.revokeRole(ROLE, authorized, { from: admin }), this.accessControl.revokeRole(ROLE, authorized, { from: admin }),
'AccessControl: sender must be an admin to revoke' 'AccessControl: sender must be an admin to revoke',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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; const [ owner, other ] = accounts;
beforeEach(async function () { beforeEach(async function () {
...@@ -28,14 +27,14 @@ describe('Ownable', function () { ...@@ -28,14 +27,14 @@ describe('Ownable', function () {
it('prevents non-owners from transferring', async function () { it('prevents non-owners from transferring', async function () {
await expectRevert( await expectRevert(
this.ownable.transferOwnership(other, { from: other }), 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 () { it('guards ownership against stuck state', async function () {
await expectRevert( await expectRevert(
this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }), 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 () { ...@@ -51,7 +50,7 @@ describe('Ownable', function () {
it('prevents non-owners from renouncement', async function () { it('prevents non-owners from renouncement', async function () {
await expectRevert( await expectRevert(
this.ownable.renounceOwnership({ from: other }), this.ownable.renounceOwnership({ from: other }),
'Ownable: caller is not the owner' 'Ownable: caller is not the owner',
); );
}); });
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const { toEthSignedMessageHash, fixSignature } = require('../helpers/sign'); const { toEthSignedMessageHash, fixSignature } = require('../helpers/sign');
const { expect } = require('chai'); const { expect } = require('chai');
const ECDSAMock = contract.fromArtifact('ECDSAMock'); const ECDSAMock = artifacts.require('ECDSAMock');
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin');
const WRONG_MESSAGE = web3.utils.sha3('Nope'); const WRONG_MESSAGE = web3.utils.sha3('Nope');
describe('ECDSA', function () { contract('ECDSA', function (accounts) {
const [ other ] = accounts; const [ other ] = accounts;
beforeEach(async function () { beforeEach(async function () {
...@@ -26,7 +24,7 @@ describe('ECDSA', function () { ...@@ -26,7 +24,7 @@ describe('ECDSA', function () {
await expectRevert( await expectRevert(
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
this.ecdsa.recover(TEST_MESSAGE, '0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'), this.ecdsa.recover(TEST_MESSAGE, '0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'),
'ECDSA: invalid signature length' 'ECDSA: invalid signature length',
); );
}); });
}); });
...@@ -116,7 +114,7 @@ describe('ECDSA', function () { ...@@ -116,7 +114,7 @@ describe('ECDSA', function () {
// Recover the signer address from the generated message and signature. // Recover the signer address from the generated message and signature.
expect(await this.ecdsa.recover( expect(await this.ecdsa.recover(
toEthSignedMessageHash(TEST_MESSAGE), toEthSignedMessageHash(TEST_MESSAGE),
signature signature,
)).to.equal(other); )).to.equal(other);
}); });
}); });
......
const { contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers'); require('@openzeppelin/test-helpers');
const { MerkleTree } = require('../helpers/merkleTree.js'); const { MerkleTree } = require('../helpers/merkleTree.js');
...@@ -7,9 +5,9 @@ const { keccakFromString, bufferToHex } = require('ethereumjs-util'); ...@@ -7,9 +5,9 @@ const { keccakFromString, bufferToHex } = require('ethereumjs-util');
const { expect } = require('chai'); const { expect } = require('chai');
const MerkleProofWrapper = contract.fromArtifact('MerkleProofWrapper'); const MerkleProofWrapper = artifacts.require('MerkleProofWrapper');
describe('MerkleProof', function () { contract('MerkleProof', function (accounts) {
beforeEach(async function () { beforeEach(async function () {
this.merkleProof = await MerkleProofWrapper.new(); this.merkleProof = await MerkleProofWrapper.new();
}); });
......
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 };
const { web3 } = require('@openzeppelin/test-environment');
function toEthSignedMessageHash (messageHex) { function toEthSignedMessageHash (messageHex) {
const messageBuffer = Buffer.from(messageHex.substring(2), 'hex'); const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`); const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
...@@ -47,7 +45,7 @@ const getSignFor = (contract, signer) => (redeemer, methodName, methodArgs = []) ...@@ -47,7 +45,7 @@ const getSignFor = (contract, signer) => (redeemer, methodName, methodArgs = [])
if (methodArgs.length > 0) { if (methodArgs.length > 0) {
parts.push( parts.push(
contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE])).encodeABI() contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE])).encodeABI()
.slice(0, -1 * PADDED_SIGNATURE_SIZE) .slice(0, -1 * PADDED_SIGNATURE_SIZE),
); );
} else { } else {
const abi = contract.abi.find(abi => abi.name === methodName); const abi = contract.abi.find(abi => abi.name === methodName);
......
const { contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const { shouldSupportInterfaces } = require('./SupportsInterface.behavior'); 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 () { beforeEach(async function () {
this.mock = await ERC165Mock.new(); this.mock = await ERC165Mock.new();
}); });
......
const { contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers'); require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const ERC165CheckerMock = contract.fromArtifact('ERC165CheckerMock'); const ERC165CheckerMock = artifacts.require('ERC165CheckerMock');
const ERC165NotSupported = contract.fromArtifact('ERC165NotSupported'); const ERC165NotSupported = artifacts.require('ERC165NotSupported');
const ERC165InterfacesSupported = contract.fromArtifact('ERC165InterfacesSupported'); const ERC165InterfacesSupported = artifacts.require('ERC165InterfacesSupported');
const DUMMY_ID = '0xdeadbeef'; const DUMMY_ID = '0xdeadbeef';
const DUMMY_ID_2 = '0xcafebabe'; const DUMMY_ID_2 = '0xcafebabe';
...@@ -14,7 +13,7 @@ const DUMMY_UNSUPPORTED_ID = '0xbaddcafe'; ...@@ -14,7 +13,7 @@ const DUMMY_UNSUPPORTED_ID = '0xbaddcafe';
const DUMMY_UNSUPPORTED_ID_2 = '0xbaadcafe'; const DUMMY_UNSUPPORTED_ID_2 = '0xbaadcafe';
const DUMMY_ACCOUNT = '0x1111111111111111111111111111111111111111'; const DUMMY_ACCOUNT = '0x1111111111111111111111111111111111111111';
describe('ERC165Checker', function () { contract('ERC165Checker', function (accounts) {
beforeEach(async function () { beforeEach(async function () {
this.mock = await ERC165CheckerMock.new(); this.mock = await ERC165CheckerMock.new();
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert, singletons } = require('@openzeppelin/test-helpers'); const { expectRevert, singletons } = require('@openzeppelin/test-helpers');
const { bufferToHex, keccakFromString } = require('ethereumjs-util'); const { bufferToHex, keccakFromString } = require('ethereumjs-util');
const { expect } = require('chai'); 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 [ registryFunder, implementee, other ] = accounts;
const ERC1820_ACCEPT_MAGIC = bufferToHex(keccakFromString('ERC1820_ACCEPT_MAGIC')); const ERC1820_ACCEPT_MAGIC = bufferToHex(keccakFromString('ERC1820_ACCEPT_MAGIC'));
...@@ -29,9 +27,9 @@ describe('ERC1820Implementer', function () { ...@@ -29,9 +27,9 @@ describe('ERC1820Implementer', function () {
it('reverts when attempting to set as implementer in the registry', async function () { it('reverts when attempting to set as implementer in the registry', async function () {
await expectRevert( await expectRevert(
this.registry.setInterfaceImplementer( 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 () { ...@@ -58,7 +56,7 @@ describe('ERC1820Implementer', function () {
it('can be set as an implementer for supported interfaces in the registry', async function () { it('can be set as an implementer for supported interfaces in the registry', async function () {
await this.registry.setInterfaceImplementer( 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)) expect(await this.registry.getInterfaceImplementer(implementee, this.interfaceA))
......
...@@ -35,6 +35,10 @@ const INTERFACES = { ...@@ -35,6 +35,10 @@ const INTERFACES = {
'safeTransferFrom(address,address,uint256,uint256,bytes)', 'safeTransferFrom(address,address,uint256,uint256,bytes)',
'safeBatchTransferFrom(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 = {}; const INTERFACE_IDS = {};
...@@ -50,7 +54,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) { ...@@ -50,7 +54,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) {
function shouldSupportInterfaces (interfaces = []) { function shouldSupportInterfaces (interfaces = []) {
describe('Contract interface', function () { describe('Contract interface', function () {
beforeEach(function () { beforeEach(function () {
this.contractUnderTest = this.mock || this.token; this.contractUnderTest = this.mock || this.token || this.holder;
}); });
for (const k of interfaces) { for (const k of interfaces) {
......
const { contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers'); const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 min = new BN('1234');
const max = new BN('5678'); const max = new BN('5678');
......
const { contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { MAX_UINT256 } = constants; const { MAX_UINT256 } = constants;
const { expect } = require('chai'); const { expect } = require('chai');
const SafeMathMock = contract.fromArtifact('SafeMathMock'); const SafeMathMock = artifacts.require('SafeMathMock');
describe('SafeMath', function () { contract('SafeMath', function (accounts) {
beforeEach(async function () { beforeEach(async function () {
this.safeMath = await SafeMathMock.new(); this.safeMath = await SafeMathMock.new();
}); });
......
const { contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { MAX_INT256, MIN_INT256 } = constants; const { MAX_INT256, MIN_INT256 } = constants;
const { expect } = require('chai'); const { expect } = require('chai');
const SignedSafeMathMock = contract.fromArtifact('SignedSafeMathMock'); const SignedSafeMathMock = artifacts.require('SignedSafeMathMock');
describe('SignedSafeMath', function () { contract('SignedSafeMath', function (accounts) {
beforeEach(async function () { beforeEach(async function () {
this.safeMath = await SignedSafeMathMock.new(); this.safeMath = await SignedSafeMathMock.new();
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, constants, ether, expectEvent, send, expectRevert } = require('@openzeppelin/test-helpers'); const { balance, constants, ether, expectEvent, send, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ owner, payee1, payee2, payee3, nonpayee1, payer1 ] = accounts;
const amount = ether('1'); const amount = ether('1');
...@@ -18,31 +16,31 @@ describe('PaymentSplitter', function () { ...@@ -18,31 +16,31 @@ describe('PaymentSplitter', function () {
it('rejects more payees than shares', async function () { it('rejects more payees than shares', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2, payee3], [20, 30]), 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 () { it('rejects more shares than payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 30, 40]), 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 () { it('rejects null payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, ZERO_ADDRESS], [20, 30]), 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 () { it('rejects zero-valued shares', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 0]), await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 0]),
'PaymentSplitter: shares are 0' 'PaymentSplitter: shares are 0',
); );
}); });
it('rejects repeated payees', async function () { it('rejects repeated payees', async function () {
await expectRevert(PaymentSplitter.new([payee1, payee1], [20, 30]), 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 () { ...@@ -84,13 +82,13 @@ describe('PaymentSplitter', function () {
describe('release', async function () { describe('release', async function () {
it('reverts if no funds to claim', async function () { it('reverts if no funds to claim', async function () {
await expectRevert(this.contract.release(payee1), 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 () { it('reverts if non-payee want to claim', async function () {
await send.ether(payer1, this.contract.address, amount); await send.ether(payer1, this.contract.address, amount);
await expectRevert(this.contract.release(nonpayee1), await expectRevert(this.contract.release(nonpayee1),
'PaymentSplitter: account has no shares' 'PaymentSplitter: account has no shares',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, ether } = require('@openzeppelin/test-helpers'); const { balance, ether } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 [ payer, payee1, payee2 ] = accounts;
const amount = ether('17'); const amount = ether('17');
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { ether, expectRevert } = require('@openzeppelin/test-helpers'); const { ether, expectRevert } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); 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; const [ owner, payee, ...otherAccounts ] = accounts;
beforeEach(async function () { beforeEach(async function () {
...@@ -31,7 +29,7 @@ describe('ConditionalEscrow', function () { ...@@ -31,7 +29,7 @@ describe('ConditionalEscrow', function () {
await this.escrow.deposit(payee, { from: owner, value: amount }); await this.escrow.deposit(payee, { from: owner, value: amount });
await expectRevert(this.escrow.withdraw(payee, { from: owner }), 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]) { ...@@ -21,7 +21,7 @@ function shouldBehaveLikeEscrow (owner, [payee1, payee2]) {
it('only the owner can deposit', async function () { it('only the owner can deposit', async function () {
await expectRevert(this.escrow.deposit(payee1, { from: payee2 }), 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]) { ...@@ -73,7 +73,7 @@ function shouldBehaveLikeEscrow (owner, [payee1, payee2]) {
it('only the owner can withdraw', async function () { it('only the owner can withdraw', async function () {
await expectRevert(this.escrow.withdraw(payee1, { from: payee1 }), await expectRevert(this.escrow.withdraw(payee1, { from: payee1 }),
'Ownable: caller is not the owner' 'Ownable: caller is not the owner',
); );
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
require('@openzeppelin/test-helpers'); require('@openzeppelin/test-helpers');
const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); 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; const [ owner, ...otherAccounts ] = accounts;
beforeEach(async function () { beforeEach(async function () {
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { balance, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { balance, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ owner, beneficiary, refundee1, refundee2 ] = accounts;
const amount = ether('54'); const amount = ether('54');
...@@ -15,7 +13,7 @@ describe('RefundEscrow', function () { ...@@ -15,7 +13,7 @@ describe('RefundEscrow', function () {
it('requires a non-null beneficiary', async function () { it('requires a non-null beneficiary', async function () {
await expectRevert( 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 () { ...@@ -39,21 +37,21 @@ describe('RefundEscrow', function () {
it('does not refund refundees', async function () { it('does not refund refundees', async function () {
await this.escrow.deposit(refundee1, { from: owner, value: amount }); await this.escrow.deposit(refundee1, { from: owner, value: amount });
await expectRevert(this.escrow.withdraw(refundee1), 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 () { it('does not allow beneficiary withdrawal', async function () {
await this.escrow.deposit(refundee1, { from: owner, value: amount }); await this.escrow.deposit(refundee1, { from: owner, value: amount });
await expectRevert(this.escrow.beneficiaryWithdraw(), 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 () { it('only the owner can enter closed state', async function () {
await expectRevert(this.escrow.close({ from: beneficiary }), 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 }); const { logs } = await this.escrow.close({ from: owner });
...@@ -69,13 +67,13 @@ describe('RefundEscrow', function () { ...@@ -69,13 +67,13 @@ describe('RefundEscrow', function () {
it('rejects deposits', async function () { it('rejects deposits', async function () {
await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }), 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 () { it('does not refund refundees', async function () {
await expectRevert(this.escrow.withdraw(refundee1), 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 () { ...@@ -87,20 +85,20 @@ describe('RefundEscrow', function () {
it('prevents entering the refund state', async function () { it('prevents entering the refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: owner }), 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 () { it('prevents re-entering the closed state', async function () {
await expectRevert(this.escrow.close({ from: owner }), 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 () { it('only the owner can enter refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: beneficiary }), 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 }); const { logs } = await this.escrow.enableRefunds({ from: owner });
...@@ -116,7 +114,7 @@ describe('RefundEscrow', function () { ...@@ -116,7 +114,7 @@ describe('RefundEscrow', function () {
it('rejects deposits', async function () { it('rejects deposits', async function () {
await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }), 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 () { ...@@ -130,19 +128,19 @@ describe('RefundEscrow', function () {
it('does not allow beneficiary withdrawal', async function () { it('does not allow beneficiary withdrawal', async function () {
await expectRevert(this.escrow.beneficiaryWithdraw(), 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 () { it('prevents entering the closed state', async function () {
await expectRevert(this.escrow.close({ from: owner }), 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 () { it('prevents re-entering the refund state', async function () {
await expectRevert(this.escrow.enableRefunds({ from: owner }), await expectRevert(this.escrow.enableRefunds({ from: owner }),
'RefundEscrow: can only enable refunds while active' 'RefundEscrow: can only enable refunds while active',
); );
}); });
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ deployer, other ] = accounts;
const firstTokenId = new BN('845'); const firstTokenId = new BN('845');
...@@ -50,7 +48,7 @@ describe('ERC1155PresetMinterPauser', function () { ...@@ -50,7 +48,7 @@ describe('ERC1155PresetMinterPauser', function () {
it('deployer can mint tokens', async function () { it('deployer can mint tokens', async function () {
const receipt = await this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }); const receipt = await this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer });
expectEvent(receipt, 'TransferSingle', 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); expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount);
...@@ -59,7 +57,7 @@ describe('ERC1155PresetMinterPauser', function () { ...@@ -59,7 +57,7 @@ describe('ERC1155PresetMinterPauser', function () {
it('other accounts cannot mint tokens', async function () { it('other accounts cannot mint tokens', async function () {
await expectRevert( await expectRevert(
this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: other }), 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 () { ...@@ -67,11 +65,11 @@ describe('ERC1155PresetMinterPauser', function () {
describe('batched minting', function () { describe('batched minting', function () {
it('deployer can batch mint tokens', async function () { it('deployer can batch mint tokens', async function () {
const receipt = await this.token.mintBatch( 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', 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); expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount);
...@@ -80,9 +78,9 @@ describe('ERC1155PresetMinterPauser', function () { ...@@ -80,9 +78,9 @@ describe('ERC1155PresetMinterPauser', function () {
it('other accounts cannot batch mint tokens', async function () { it('other accounts cannot batch mint tokens', async function () {
await expectRevert( await expectRevert(
this.token.mintBatch( 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 () { ...@@ -109,14 +107,14 @@ describe('ERC1155PresetMinterPauser', function () {
await expectRevert( await expectRevert(
this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }), 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 () { it('other accounts cannot pause', async function () {
await expectRevert( await expectRevert(
this.token.pause({ from: other }), 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 () { ...@@ -127,7 +125,7 @@ describe('ERC1155PresetMinterPauser', function () {
const receipt = await this.token.burn(other, firstTokenId, firstTokenIdAmount.subn(1), { from: other }); const receipt = await this.token.burn(other, firstTokenId, firstTokenIdAmount.subn(1), { from: other });
expectEvent(receipt, 'TransferSingle', 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'); expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal('1');
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ deployer, other ] = accounts;
const name = 'MinterPauserToken'; const name = 'MinterPauserToken';
...@@ -54,7 +52,7 @@ describe('ERC20PresetMinterPauser', function () { ...@@ -54,7 +52,7 @@ describe('ERC20PresetMinterPauser', function () {
it('other accounts cannot mint tokens', async function () { it('other accounts cannot mint tokens', async function () {
await expectRevert( await expectRevert(
this.token.mint(other, amount, { from: other }), 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 () { ...@@ -81,7 +79,7 @@ describe('ERC20PresetMinterPauser', function () {
await expectRevert( await expectRevert(
this.token.mint(other, amount, { from: deployer }), this.token.mint(other, amount, { from: deployer }),
'ERC20Pausable: token transfer while paused' 'ERC20Pausable: token transfer while paused',
); );
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ deployer, other ] = accounts;
const name = 'MinterAutoIDToken'; const name = 'MinterAutoIDToken';
...@@ -63,7 +61,7 @@ describe('ERC721PresetMinterPauserAutoId', function () { ...@@ -63,7 +61,7 @@ describe('ERC721PresetMinterPauserAutoId', function () {
it('other accounts cannot mint tokens', async function () { it('other accounts cannot mint tokens', async function () {
await expectRevert( await expectRevert(
this.token.mint(other, { from: other }), 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 () { ...@@ -90,14 +88,14 @@ describe('ERC721PresetMinterPauserAutoId', function () {
await expectRevert( await expectRevert(
this.token.mint(other, { from: deployer }), this.token.mint(other, { from: deployer }),
'ERC721Pausable: token transfer while paused' 'ERC721Pausable: token transfer while paused',
); );
}); });
it('other accounts cannot pause', async function () { it('other accounts cannot pause', async function () {
await expectRevert( await expectRevert(
this.token.pause({ from: other }), this.token.pause({ from: other }),
'ERC721PresetMinterPauserAutoId: must have pauser role to pause' 'ERC721PresetMinterPauserAutoId: must have pauser role to pause',
); );
}); });
}); });
......
const { contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const { assert } = require('chai'); const { assert } = require('chai');
const InitializableMock = contract.fromArtifact('InitializableMock'); const InitializableMock = artifacts.require('InitializableMock');
const SampleChild = contract.fromArtifact('SampleChild'); const SampleChild = artifacts.require('SampleChild');
describe('Initializable', function () { contract('Initializable', function (accounts) {
describe('basic testing without inheritance', function () { describe('basic testing without inheritance', function () {
beforeEach('deploying', async function () { beforeEach('deploying', async function () {
this.contract = await InitializableMock.new(); this.contract = await InitializableMock.new();
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const ImplV1 = contract.fromArtifact('DummyImplementation'); const ImplV1 = artifacts.require('DummyImplementation');
const ImplV2 = contract.fromArtifact('DummyImplementationV2'); const ImplV2 = artifacts.require('DummyImplementationV2');
const ProxyAdmin = contract.fromArtifact('ProxyAdmin'); const ProxyAdmin = artifacts.require('ProxyAdmin');
const TransparentUpgradeableProxy = contract.fromArtifact('TransparentUpgradeableProxy'); const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy');
describe('ProxyAdmin', function () { contract('ProxyAdmin', function (accounts) {
const [proxyAdminOwner, newAdmin, anotherAccount] = accounts; const [proxyAdminOwner, newAdmin, anotherAccount] = accounts;
before('set implementations', async function () { before('set implementations', async function () {
...@@ -85,7 +83,7 @@ describe('ProxyAdmin', function () { ...@@ -85,7 +83,7 @@ describe('ProxyAdmin', function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI(); const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await expectRevert( await expectRevert(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: anotherAccount } { from: anotherAccount },
), ),
'caller is not the owner', 'caller is not the owner',
); );
...@@ -98,7 +96,7 @@ describe('ProxyAdmin', function () { ...@@ -98,7 +96,7 @@ describe('ProxyAdmin', function () {
const callData = '0x12345678'; const callData = '0x12345678';
await expectRevert.unspecified( await expectRevert.unspecified(
this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner } { from: proxyAdminOwner },
), ),
); );
}); });
...@@ -108,7 +106,7 @@ describe('ProxyAdmin', function () { ...@@ -108,7 +106,7 @@ describe('ProxyAdmin', function () {
it('upgrades implementation', async function () { it('upgrades implementation', async function () {
const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI(); const callData = new ImplV1('').contract.methods['initializeNonPayable(uint256)'](1337).encodeABI();
await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData,
{ from: proxyAdminOwner } { from: proxyAdminOwner },
); );
const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address);
expect(implementationAddress).to.be.equal(this.implementationV2.address); expect(implementationAddress).to.be.equal(this.implementationV2.address);
......
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers'); const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { toChecksumAddress, keccak256 } = require('ethereumjs-util'); const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai'); const { expect } = require('chai');
const Proxy = contract.fromArtifact('Proxy'); const Proxy = artifacts.require('Proxy');
const Implementation1 = contract.fromArtifact('Implementation1'); const Implementation1 = artifacts.require('Implementation1');
const Implementation2 = contract.fromArtifact('Implementation2'); const Implementation2 = artifacts.require('Implementation2');
const Implementation3 = contract.fromArtifact('Implementation3'); const Implementation3 = artifacts.require('Implementation3');
const Implementation4 = contract.fromArtifact('Implementation4'); const Implementation4 = artifacts.require('Implementation4');
const MigratableMockV1 = contract.fromArtifact('MigratableMockV1'); const MigratableMockV1 = artifacts.require('MigratableMockV1');
const MigratableMockV2 = contract.fromArtifact('MigratableMockV2'); const MigratableMockV2 = artifacts.require('MigratableMockV2');
const MigratableMockV3 = contract.fromArtifact('MigratableMockV3'); const MigratableMockV3 = artifacts.require('MigratableMockV3');
const InitializableMock = contract.fromArtifact('InitializableMock'); const InitializableMock = artifacts.require('InitializableMock');
const DummyImplementation = contract.fromArtifact('DummyImplementation'); const DummyImplementation = artifacts.require('DummyImplementation');
const ClashingImplementation = contract.fromArtifact('ClashingImplementation'); const ClashingImplementation = artifacts.require('ClashingImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation'; const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
const ADMIN_LABEL = 'eip1967.proxy.admin'; const ADMIN_LABEL = 'eip1967.proxy.admin';
...@@ -89,7 +87,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro ...@@ -89,7 +87,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
it('reverts', async function () { it('reverts', async function () {
await expectRevert.unspecified( await expectRevert.unspecified(
this.proxy.upgradeTo(this.implementationV1, { from }) this.proxy.upgradeTo(this.implementationV1, { from }),
); );
}); });
}); });
...@@ -409,7 +407,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro ...@@ -409,7 +407,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
await proxy.upgradeTo(instance4.address, { from: proxyAdminAddress }); await proxy.upgradeTo(instance4.address, { from: proxyAdminAddress });
const proxyInstance4 = new Implementation4(proxy.address); const proxyInstance4 = new Implementation4(proxy.address);
const data = ''; const data = '0x';
await web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }); await web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data });
const res = await proxyInstance4.getValue(); const res = await proxyInstance4.getValue();
...@@ -423,7 +421,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro ...@@ -423,7 +421,7 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
const instance2 = await Implementation2.new(); const instance2 = await Implementation2.new();
await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress }); await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress });
const data = ''; const data = '0x';
await expectRevert.unspecified( await expectRevert.unspecified(
web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }), web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }),
); );
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour'); const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour');
const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.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 [proxyAdminAddress, proxyAdminOwner] = accounts;
const createProxy = async function (logic, admin, initData, opts) { const createProxy = async function (logic, admin, initData, opts) {
......
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { toChecksumAddress, keccak256 } = require('ethereumjs-util'); const { toChecksumAddress, keccak256 } = require('ethereumjs-util');
const { expect } = require('chai'); const { expect } = require('chai');
const DummyImplementation = contract.fromArtifact('DummyImplementation'); const DummyImplementation = artifacts.require('DummyImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation'; const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const shouldBehaveLikeUpgradeableProxy = require('./UpgradeableProxy.behaviour'); 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 [proxyAdminOwner] = accounts;
const createProxy = async function (implementation, _admin, initData, opts) { const createProxy = async function (implementation, _admin, initData, opts) {
......
const { defaultSender, web3 } = require('@openzeppelin/test-environment');
const { deployRelayHub } = require('@openzeppelin/gsn-helpers'); const { deployRelayHub } = require('@openzeppelin/gsn-helpers');
before('deploy GSN RelayHub', async function () { before('deploy GSN RelayHub', async function () {
const [defaultSender] = await web3.eth.getAccounts();
await deployRelayHub(web3, { from: defaultSender }); await deployRelayHub(web3, { from: defaultSender });
}); });
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); const { expect } = require('chai');
const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior'); 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 [operator, tokenHolder, tokenBatchHolder, ...otherAccounts] = accounts;
const initialURI = 'https://token-cdn-domain/{id}.json'; const initialURI = 'https://token-cdn-domain/{id}.json';
...@@ -34,7 +32,7 @@ describe('ERC1155', function () { ...@@ -34,7 +32,7 @@ describe('ERC1155', function () {
it('reverts with a zero destination address', async function () { it('reverts with a zero destination address', async function () {
await expectRevert( await expectRevert(
this.token.mint(ZERO_ADDRESS, tokenId, mintAmount, data), 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 () { ...@@ -63,19 +61,19 @@ describe('ERC1155', function () {
it('reverts with a zero destination address', async function () { it('reverts with a zero destination address', async function () {
await expectRevert( await expectRevert(
this.token.mintBatch(ZERO_ADDRESS, tokenBatchIds, mintAmounts, data), 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 () { it('reverts if length of inputs do not match', async function () {
await expectRevert( await expectRevert(
this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts.slice(1), data), this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts.slice(1), data),
'ERC1155: ids and amounts length mismatch' 'ERC1155: ids and amounts length mismatch',
); );
await expectRevert( await expectRevert(
this.token.mintBatch(tokenBatchHolder, tokenBatchIds.slice(1), mintAmounts, data), 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 () { ...@@ -86,7 +84,7 @@ describe('ERC1155', function () {
tokenBatchIds, tokenBatchIds,
mintAmounts, mintAmounts,
data, data,
{ from: operator } { from: operator },
)); ));
}); });
...@@ -101,7 +99,7 @@ describe('ERC1155', function () { ...@@ -101,7 +99,7 @@ describe('ERC1155', function () {
it('credits the minted batch of tokens', async function () { it('credits the minted batch of tokens', async function () {
const holderBatchBalances = await this.token.balanceOfBatch( const holderBatchBalances = await this.token.balanceOfBatch(
new Array(tokenBatchIds.length).fill(tokenBatchHolder), new Array(tokenBatchIds.length).fill(tokenBatchHolder),
tokenBatchIds tokenBatchIds,
); );
for (let i = 0; i < holderBatchBalances.length; i++) { for (let i = 0; i < holderBatchBalances.length; i++) {
...@@ -115,14 +113,14 @@ describe('ERC1155', function () { ...@@ -115,14 +113,14 @@ describe('ERC1155', function () {
it('reverts when burning the zero account\'s tokens', async function () { it('reverts when burning the zero account\'s tokens', async function () {
await expectRevert( await expectRevert(
this.token.burn(ZERO_ADDRESS, tokenId, mintAmount), 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 () { it('reverts when burning a non-existent token id', async function () {
await expectRevert( await expectRevert(
this.token.burn(tokenHolder, tokenId, mintAmount), this.token.burn(tokenHolder, tokenId, mintAmount),
'ERC1155: burn amount exceeds balance' 'ERC1155: burn amount exceeds balance',
); );
}); });
...@@ -132,12 +130,12 @@ describe('ERC1155', function () { ...@@ -132,12 +130,12 @@ describe('ERC1155', function () {
tokenId, tokenId,
mintAmount, mintAmount,
data, data,
{ from: operator } { from: operator },
); );
await expectRevert( await expectRevert(
this.token.burn(tokenHolder, tokenId, mintAmount.addn(1)), 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 () { ...@@ -148,7 +146,7 @@ describe('ERC1155', function () {
tokenHolder, tokenHolder,
tokenId, tokenId,
burnAmount, burnAmount,
{ from: operator } { from: operator },
)); ));
}); });
...@@ -165,7 +163,7 @@ describe('ERC1155', function () { ...@@ -165,7 +163,7 @@ describe('ERC1155', function () {
it('accounts for both minting and burning', async function () { it('accounts for both minting and burning', async function () {
expect(await this.token.balanceOf( expect(await this.token.balanceOf(
tokenHolder, tokenHolder,
tokenId tokenId,
)).to.be.bignumber.equal(mintAmount.sub(burnAmount)); )).to.be.bignumber.equal(mintAmount.sub(burnAmount));
}); });
}); });
...@@ -175,26 +173,26 @@ describe('ERC1155', function () { ...@@ -175,26 +173,26 @@ describe('ERC1155', function () {
it('reverts when burning the zero account\'s tokens', async function () { it('reverts when burning the zero account\'s tokens', async function () {
await expectRevert( await expectRevert(
this.token.burnBatch(ZERO_ADDRESS, tokenBatchIds, burnAmounts), 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 () { it('reverts if length of inputs do not match', async function () {
await expectRevert( await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts.slice(1)), this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts.slice(1)),
'ERC1155: ids and amounts length mismatch' 'ERC1155: ids and amounts length mismatch',
); );
await expectRevert( await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds.slice(1), burnAmounts), 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 () { it('reverts when burning a non-existent token id', async function () {
await expectRevert( await expectRevert(
this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts), this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts),
'ERC1155: burn amount exceeds balance' 'ERC1155: burn amount exceeds balance',
); );
}); });
...@@ -205,7 +203,7 @@ describe('ERC1155', function () { ...@@ -205,7 +203,7 @@ describe('ERC1155', function () {
tokenBatchHolder, tokenBatchHolder,
tokenBatchIds, tokenBatchIds,
burnAmounts, burnAmounts,
{ from: operator } { from: operator },
)); ));
}); });
...@@ -222,7 +220,7 @@ describe('ERC1155', function () { ...@@ -222,7 +220,7 @@ describe('ERC1155', function () {
it('accounts for both minting and burning', async function () { it('accounts for both minting and burning', async function () {
const holderBatchBalances = await this.token.balanceOfBatch( const holderBatchBalances = await this.token.balanceOfBatch(
new Array(tokenBatchIds.length).fill(tokenBatchHolder), new Array(tokenBatchIds.length).fill(tokenBatchHolder),
tokenBatchIds tokenBatchIds,
); );
for (let i = 0; i < holderBatchBalances.length; i++) { for (let i = 0; i < holderBatchBalances.length; i++) {
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 [ holder, operator, other ] = accounts;
const uri = 'https://token.com'; const uri = 'https://token.com';
...@@ -38,7 +36,7 @@ describe('ERC1155Burnable', function () { ...@@ -38,7 +36,7 @@ describe('ERC1155Burnable', function () {
it('unapproved accounts cannot burn the holder\'s tokens', async function () { it('unapproved accounts cannot burn the holder\'s tokens', async function () {
await expectRevert( await expectRevert(
this.token.burn(holder, tokenIds[0], amounts[0].subn(1), { from: other }), 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 () { ...@@ -62,7 +60,7 @@ describe('ERC1155Burnable', function () {
it('unapproved accounts cannot burn the holder\'s tokens', async function () { it('unapproved accounts cannot burn the holder\'s tokens', async function () {
await expectRevert( await expectRevert(
this.token.burnBatch(holder, tokenIds, [ amounts[0].subn(1), amounts[1].subn(2) ], { from: other }), 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',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers'); const { BN } = require('@openzeppelin/test-helpers');
const ERC1155Holder = contract.fromArtifact('ERC1155Holder'); const ERC1155Holder = artifacts.require('ERC1155Holder');
const ERC1155Mock = contract.fromArtifact('ERC1155Mock'); const ERC1155Mock = artifacts.require('ERC1155Mock');
const { expect } = require('chai'); const { expect } = require('chai');
describe('ERC1155Holder', function () { const { shouldSupportInterfaces } = require('../../introspection/SupportsInterface.behavior');
contract('ERC1155Holder', function (accounts) {
const [creator] = accounts; const [creator] = accounts;
const uri = 'https://token-cdn-domain/{id}.json'; const uri = 'https://token-cdn-domain/{id}.json';
const multiTokenIds = [new BN(1), new BN(2), new BN(3)]; const multiTokenIds = [new BN(1), new BN(2), new BN(3)];
...@@ -19,6 +20,8 @@ describe('ERC1155Holder', function () { ...@@ -19,6 +20,8 @@ describe('ERC1155Holder', function () {
await this.multiToken.mintBatch(creator, multiTokenIds, multiTokenAmounts, '0x', { from: creator }); await this.multiToken.mintBatch(creator, multiTokenIds, multiTokenAmounts, '0x', { from: creator });
}); });
shouldSupportInterfaces(['ERC165', 'ERC1155Receiver']);
it('receives ERC1155 tokens from a single ID', async function () { it('receives ERC1155 tokens from a single ID', async function () {
await this.multiToken.safeTransferFrom( await this.multiToken.safeTransferFrom(
creator, creator,
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 [ holder, operator, receiver, other ] = accounts;
const uri = 'https://token.com'; const uri = 'https://token.com';
...@@ -32,58 +30,58 @@ describe('ERC1155Pausable', function () { ...@@ -32,58 +30,58 @@ describe('ERC1155Pausable', function () {
it('reverts when trying to safeTransferFrom from holder', async function () { it('reverts when trying to safeTransferFrom from holder', async function () {
await expectRevert( await expectRevert(
this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: holder }), 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 () { it('reverts when trying to safeTransferFrom from operator', async function () {
await expectRevert( await expectRevert(
this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: operator }), 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 () { it('reverts when trying to safeBatchTransferFrom from holder', async function () {
await expectRevert( await expectRevert(
this.token.safeBatchTransferFrom(holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: holder }), 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 () { it('reverts when trying to safeBatchTransferFrom from operator', async function () {
await expectRevert( await expectRevert(
this.token.safeBatchTransferFrom( 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 () { it('reverts when trying to mint', async function () {
await expectRevert( await expectRevert(
this.token.mint(holder, secondTokenId, secondTokenAmount, '0x'), 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 () { it('reverts when trying to mintBatch', async function () {
await expectRevert( await expectRevert(
this.token.mintBatch(holder, [secondTokenId], [secondTokenAmount], '0x'), 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 () { it('reverts when trying to burn', async function () {
await expectRevert( await expectRevert(
this.token.burn(holder, firstTokenId, firstTokenAmount), this.token.burn(holder, firstTokenId, firstTokenAmount),
'ERC1155Pausable: token transfer while paused' 'ERC1155Pausable: token transfer while paused',
); );
}); });
it('reverts when trying to burnBatch', async function () { it('reverts when trying to burnBatch', async function () {
await expectRevert( await expectRevert(
this.token.burn(holder, [firstTokenId], [firstTokenAmount]), 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 ...@@ -27,7 +27,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply, shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply,
function (from, to, value) { function (from, to, value) {
return this.token.transfer(to, value, { from }); return this.token.transfer(to, value, { from });
} },
); );
}); });
...@@ -88,7 +88,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip ...@@ -88,7 +88,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferFrom( 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 ...@@ -104,7 +104,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferFrom( 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 ...@@ -114,7 +114,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferFrom( 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 ...@@ -131,7 +131,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferFrom( 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 ...@@ -144,7 +144,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferFrom( 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 ...@@ -154,7 +154,7 @@ function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recip
shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply, shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply,
function (owner, spender, amount) { function (owner, spender, amount) {
return this.token.approve(spender, amount, { from: owner }); return this.token.approve(spender, amount, { from: owner });
} },
); );
}); });
} }
...@@ -166,7 +166,7 @@ function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer ...@@ -166,7 +166,7 @@ function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer
it('reverts', async function () { it('reverts', async function () {
await expectRevert(transfer.call(this, from, to, amount), 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 ...@@ -219,7 +219,7 @@ function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer
describe('when the recipient is the zero address', function () { describe('when the recipient is the zero address', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(transfer.call(this, from, ZERO_ADDRESS, balance), 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 ...@@ -299,7 +299,7 @@ function shouldBehaveLikeERC20Approve (errorPrefix, owner, spender, supply, appr
describe('when the spender is the zero address', function () { describe('when the spender is the zero address', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(approve.call(this, owner, ZERO_ADDRESS, supply), await expectRevert(approve.call(this, owner, ZERO_ADDRESS, supply),
`${errorPrefix}: approve to the zero address` `${errorPrefix}: approve to the zero address`,
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
...@@ -10,10 +8,10 @@ const { ...@@ -10,10 +8,10 @@ const {
shouldBehaveLikeERC20Approve, shouldBehaveLikeERC20Approve,
} = require('./ERC20.behavior'); } = require('./ERC20.behavior');
const ERC20Mock = contract.fromArtifact('ERC20Mock'); const ERC20Mock = artifacts.require('ERC20Mock');
const ERC20DecimalsMock = contract.fromArtifact('ERC20DecimalsMock'); const ERC20DecimalsMock = artifacts.require('ERC20DecimalsMock');
describe('ERC20', function () { contract('ERC20', function (accounts) {
const [ initialHolder, recipient, anotherAccount ] = accounts; const [ initialHolder, recipient, anotherAccount ] = accounts;
const name = 'My Token'; const name = 'My Token';
...@@ -56,7 +54,7 @@ describe('ERC20', function () { ...@@ -56,7 +54,7 @@ describe('ERC20', function () {
describe('when there was no approved amount before', function () { describe('when there was no approved amount before', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.decreaseAllowance( 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 () { ...@@ -92,7 +90,7 @@ describe('ERC20', function () {
it('reverts when more than the full allowance is removed', async function () { it('reverts when more than the full allowance is removed', async function () {
await expectRevert( await expectRevert(
this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }), 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 () { ...@@ -117,7 +115,7 @@ describe('ERC20', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.decreaseAllowance( 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 () { ...@@ -201,7 +199,7 @@ describe('ERC20', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert( 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 () { ...@@ -211,7 +209,7 @@ describe('ERC20', function () {
const amount = new BN(50); const amount = new BN(50);
it('rejects a null account', async function () { it('rejects a null account', async function () {
await expectRevert( 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 () { ...@@ -250,7 +248,7 @@ describe('ERC20', function () {
describe('for a non zero account', function () { describe('for a non zero account', function () {
it('rejects burning more than balance', async function () { it('rejects burning more than balance', async function () {
await expectRevert(this.token.burn( 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 () { ...@@ -295,7 +293,7 @@ describe('ERC20', function () {
describe('when the sender is the zero address', function () { describe('when the sender is the zero address', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.transferInternal(ZERO_ADDRESS, recipient, initialSupply), 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 () { ...@@ -309,7 +307,7 @@ describe('ERC20', function () {
describe('when the owner is the zero address', function () { describe('when the owner is the zero address', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply), await expectRevert(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply),
'ERC20: approve from the zero address' 'ERC20: approve from the zero address',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers'); const { BN } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Burnable } = require('./behaviors/ERC20Burnable.behavior'); 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 [ owner, ...otherAccounts ] = accounts;
const initialBalance = new BN(1000); const initialBalance = new BN(1000);
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers');
const { shouldBehaveLikeERC20Capped } = require('./behaviors/ERC20Capped.behavior'); 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 [ minter, ...otherAccounts ] = accounts;
const cap = ether('1000'); const cap = ether('1000');
...@@ -15,7 +13,7 @@ describe('ERC20Capped', function () { ...@@ -15,7 +13,7 @@ describe('ERC20Capped', function () {
it('requires a non-zero cap', async function () { it('requires a non-zero cap', async function () {
await expectRevert( 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',
); );
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); 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 [ holder, recipient, anotherAccount ] = accounts;
const initialSupply = new BN(100); const initialSupply = new BN(100);
...@@ -41,7 +39,7 @@ describe('ERC20Pausable', function () { ...@@ -41,7 +39,7 @@ describe('ERC20Pausable', function () {
await this.token.pause(); await this.token.pause();
await expectRevert(this.token.transfer(recipient, initialSupply, { from: holder }), 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 () { ...@@ -74,7 +72,7 @@ describe('ERC20Pausable', function () {
await this.token.pause(); await this.token.pause();
await expectRevert(this.token.transferFrom( 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 () { ...@@ -101,7 +99,7 @@ describe('ERC20Pausable', function () {
await this.token.pause(); await this.token.pause();
await expectRevert(this.token.mint(recipient, amount), await expectRevert(this.token.mint(recipient, amount),
'ERC20Pausable: token transfer while paused' 'ERC20Pausable: token transfer while paused',
); );
}); });
}); });
...@@ -128,7 +126,7 @@ describe('ERC20Pausable', function () { ...@@ -128,7 +126,7 @@ describe('ERC20Pausable', function () {
await this.token.pause(); await this.token.pause();
await expectRevert(this.token.burn(holder, amount), await expectRevert(this.token.burn(holder, amount),
'ERC20Pausable: token transfer while paused' 'ERC20Pausable: token transfer while paused',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const ERC20SnapshotMock = contract.fromArtifact('ERC20SnapshotMock'); const ERC20SnapshotMock = artifacts.require('ERC20SnapshotMock');
const { expect } = require('chai'); const { expect } = require('chai');
describe('ERC20Snapshot', function () { contract('ERC20Snapshot', function (accounts) {
const [ initialHolder, recipient, other ] = accounts; const [ initialHolder, recipient, other ] = accounts;
const initialSupply = new BN(100); const initialSupply = new BN(100);
...@@ -76,7 +74,7 @@ describe('ERC20Snapshot', function () { ...@@ -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.initialSnapshotId)).to.be.bignumber.equal(initialSupply);
expect(await this.token.totalSupplyAt(this.secondSnapshotId)).to.be.bignumber.equal( 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 () { ...@@ -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(other, this.initialSnapshotId)).to.be.bignumber.equal('0');
expect(await this.token.balanceOfAt(initialHolder, this.secondSnapshotId)).to.be.bignumber.equal( 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( 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( 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 () { ...@@ -189,13 +187,13 @@ describe('ERC20Snapshot', function () {
for (const id of this.secondSnapshotIds) { for (const id of this.secondSnapshotIds) {
expect(await this.token.balanceOfAt(initialHolder, id)).to.be.bignumber.equal( 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( 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( expect(await this.token.balanceOfAt(other, id)).to.be.bignumber.equal(
await this.token.balanceOf(other) await this.token.balanceOf(other),
); );
} }
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectRevert } = require('@openzeppelin/test-helpers'); const { expectRevert } = require('@openzeppelin/test-helpers');
const ERC20ReturnFalseMock = contract.fromArtifact('ERC20ReturnFalseMock'); const ERC20ReturnFalseMock = artifacts.require('ERC20ReturnFalseMock');
const ERC20ReturnTrueMock = contract.fromArtifact('ERC20ReturnTrueMock'); const ERC20ReturnTrueMock = artifacts.require('ERC20ReturnTrueMock');
const ERC20NoReturnMock = contract.fromArtifact('ERC20NoReturnMock'); const ERC20NoReturnMock = artifacts.require('ERC20NoReturnMock');
const SafeERC20Wrapper = contract.fromArtifact('SafeERC20Wrapper'); const SafeERC20Wrapper = artifacts.require('SafeERC20Wrapper');
describe('SafeERC20', function () { contract('SafeERC20', function (accounts) {
const [ hasNoCode ] = accounts; const [ hasNoCode ] = accounts;
describe('with address that has no contract code', function () { describe('with address that has no contract code', function () {
...@@ -97,7 +95,7 @@ function shouldOnlyRevertOnErrors () { ...@@ -97,7 +95,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when decreasing the allowance', async function () { it('reverts when decreasing the allowance', async function () {
await expectRevert( await expectRevert(
this.wrapper.decreaseAllowance(10), this.wrapper.decreaseAllowance(10),
'SafeERC20: decreased allowance below zero' 'SafeERC20: decreased allowance below zero',
); );
}); });
}); });
...@@ -110,7 +108,7 @@ function shouldOnlyRevertOnErrors () { ...@@ -110,7 +108,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when approving a non-zero allowance', async function () { it('reverts when approving a non-zero allowance', async function () {
await expectRevert( await expectRevert(
this.wrapper.approve(20), 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 () { ...@@ -129,7 +127,7 @@ function shouldOnlyRevertOnErrors () {
it('reverts when decreasing the allowance to a negative value', async function () { it('reverts when decreasing the allowance to a negative value', async function () {
await expectRevert( await expectRevert(
this.wrapper.decreaseAllowance(200), this.wrapper.decreaseAllowance(200),
'SafeERC20: decreased allowance below zero' 'SafeERC20: decreased allowance below zero',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, expectRevert, time } = require('@openzeppelin/test-helpers'); const { BN, expectRevert, time } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const ERC20Mock = contract.fromArtifact('ERC20Mock'); const ERC20Mock = artifacts.require('ERC20Mock');
const TokenTimelock = contract.fromArtifact('TokenTimelock'); const TokenTimelock = artifacts.require('TokenTimelock');
describe('TokenTimelock', function () { contract('TokenTimelock', function (accounts) {
const [ beneficiary ] = accounts; const [ beneficiary ] = accounts;
const name = 'My Token'; const name = 'My Token';
...@@ -24,7 +22,7 @@ describe('TokenTimelock', function () { ...@@ -24,7 +22,7 @@ describe('TokenTimelock', function () {
const pastReleaseTime = (await time.latest()).sub(time.duration.years(1)); const pastReleaseTime = (await time.latest()).sub(time.duration.years(1));
await expectRevert( await expectRevert(
TokenTimelock.new(this.token.address, beneficiary, pastReleaseTime), 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]) { ...@@ -38,7 +38,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.burn(amount, { from: owner }), 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]) { ...@@ -87,7 +87,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () { it('reverts', async function () {
await this.token.approve(burner, amount, { from: owner }); await this.token.approve(burner, amount, { from: owner });
await expectRevert(this.token.burnFrom(owner, amount, { from: burner }), 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]) { ...@@ -98,7 +98,7 @@ function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) {
it('reverts', async function () { it('reverts', async function () {
await this.token.approve(burner, allowance, { from: owner }); await this.token.approve(burner, allowance, { from: owner });
await expectRevert(this.token.burnFrom(owner, allowance.addn(1), { from: burner }), await expectRevert(this.token.burnFrom(owner, allowance.addn(1), { from: burner }),
'ERC20: burn amount exceeds allowance' 'ERC20: burn amount exceeds allowance',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [owner, approved] = accounts;
const firstTokenId = new BN(1); const firstTokenId = new BN(1);
...@@ -40,7 +38,7 @@ describe('ERC721Burnable', function () { ...@@ -40,7 +38,7 @@ describe('ERC721Burnable', function () {
it('burns the given token ID and adjusts the balance of the owner', async function () { it('burns the given token ID and adjusts the balance of the owner', async function () {
await expectRevert( await expectRevert(
this.token.ownerOf(tokenId), 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'); expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1');
}); });
...@@ -64,7 +62,7 @@ describe('ERC721Burnable', function () { ...@@ -64,7 +62,7 @@ describe('ERC721Burnable', function () {
context('getApproved', function () { context('getApproved', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert( 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 () { ...@@ -73,7 +71,7 @@ describe('ERC721Burnable', function () {
describe('when the given token ID was not tracked by this contract', function () { describe('when the given token ID was not tracked by this contract', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert( 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',
); );
}); });
}); });
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN } = require('@openzeppelin/test-helpers'); const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const ERC721Holder = contract.fromArtifact('ERC721Holder'); const ERC721Holder = artifacts.require('ERC721Holder');
const ERC721Mock = contract.fromArtifact('ERC721Mock'); const ERC721Mock = artifacts.require('ERC721Mock');
describe('ERC721Holder', function () { contract('ERC721Holder', function (accounts) {
const [ owner ] = accounts; const [ owner ] = accounts;
const name = 'Non Fungible Token'; const name = 'Non Fungible Token';
......
const { accounts, contract } = require('@openzeppelin/test-environment');
const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); 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 [ owner, receiver, operator ] = accounts;
const name = 'Non Fungible Token'; const name = 'Non Fungible Token';
...@@ -31,36 +29,36 @@ describe('ERC721Pausable', function () { ...@@ -31,36 +29,36 @@ describe('ERC721Pausable', function () {
it('reverts when trying to transferFrom', async function () { it('reverts when trying to transferFrom', async function () {
await expectRevert( await expectRevert(
this.token.transferFrom(owner, receiver, firstTokenId, { from: owner }), 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 () { it('reverts when trying to safeTransferFrom', async function () {
await expectRevert( await expectRevert(
this.token.safeTransferFrom(owner, receiver, firstTokenId, { from: owner }), 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 () { it('reverts when trying to safeTransferFrom with data', async function () {
await expectRevert( await expectRevert(
this.token.methods['safeTransferFrom(address,address,uint256,bytes)']( this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](
owner, receiver, firstTokenId, mockData, { from: owner } owner, receiver, firstTokenId, mockData, { from: owner },
), 'ERC721Pausable: token transfer while paused' ), 'ERC721Pausable: token transfer while paused',
); );
}); });
it('reverts when trying to mint', async function () { it('reverts when trying to mint', async function () {
await expectRevert( await expectRevert(
this.token.mint(receiver, secondTokenId), this.token.mint(receiver, secondTokenId),
'ERC721Pausable: token transfer while paused' 'ERC721Pausable: token transfer while paused',
); );
}); });
it('reverts when trying to burn', async function () { it('reverts when trying to burn', async function () {
await expectRevert( await expectRevert(
this.token.burn(firstTokenId), this.token.burn(firstTokenId),
'ERC721Pausable: token transfer while paused' 'ERC721Pausable: token transfer while paused',
); );
}); });
......
const { contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { expect } = require('chai'); const { expect } = require('chai');
const ERC777SenderRecipientMock = contract.fromArtifact('ERC777SenderRecipientMock'); const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock');
function shouldBehaveLikeERC777DirectSendBurn (holder, recipient, data) { function shouldBehaveLikeERC777DirectSendBurn (holder, recipient, data) {
shouldBehaveLikeERC777DirectSend(holder, recipient, data); shouldBehaveLikeERC777DirectSend(holder, recipient, data);
...@@ -58,15 +57,15 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data, ...@@ -58,15 +57,15 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
it('reverts when sending more than the balance', async function () { it('reverts when sending more than the balance', async function () {
const balance = await this.token.balanceOf(holder); const balance = await this.token.balanceOf(holder);
await expectRevert.unspecified( 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 () { it('reverts when sending to the zero address', async function () {
await expectRevert.unspecified( await expectRevert.unspecified(
this.token.operatorSend( 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, ...@@ -78,7 +77,7 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
it('reverts when sending a non-zero amount', async function () { it('reverts when sending a non-zero amount', async function () {
await expectRevert.unspecified( 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, ...@@ -86,8 +85,8 @@ function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data,
// This is not yet reflected in the spec // This is not yet reflected in the spec
await expectRevert.unspecified( await expectRevert.unspecified(
this.token.operatorSend( 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 ...@@ -135,7 +134,7 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
it('reverts when burning more than the balance', async function () { it('reverts when burning more than the balance', async function () {
const balance = await this.token.balanceOf(holder); const balance = await this.token.balanceOf(holder);
await expectRevert.unspecified( 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 ...@@ -147,7 +146,7 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
it('reverts when burning a non-zero amount', async function () { it('reverts when burning a non-zero amount', async function () {
await expectRevert.unspecified( 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 ...@@ -155,8 +154,8 @@ function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorDat
// This is not yet reflected in the spec // This is not yet reflected in the spec
await expectRevert.unspecified( await expectRevert.unspecified(
this.token.operatorBurn( 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, ...@@ -282,7 +281,7 @@ function shouldBehaveLikeERC777InternalMint (recipient, operator, amount, data,
it('reverts when minting tokens for the zero address', async function () { it('reverts when minting tokens for the zero address', async function () {
await expectRevert.unspecified( 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 ...@@ -328,13 +327,13 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am
it('operatorSend reverts', async function () { it('operatorSend reverts', async function () {
await expectRevert.unspecified( 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 () { it('mint (internal) reverts', async function () {
await expectRevert.unspecified( 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 ...@@ -389,7 +388,7 @@ function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, am
it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () { it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () {
const { tx } = await this.token.mintInternal( 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); const postRecipientBalance = await this.token.balanceOf(this.recipient);
...@@ -422,7 +421,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope ...@@ -422,7 +421,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
it('operatorSend reverts', async function () { it('operatorSend reverts', async function () {
await expectRevert.unspecified( 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 ...@@ -432,7 +431,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
it('operatorBurn reverts', async function () { it('operatorBurn reverts', async function () {
await expectRevert.unspecified( 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 ...@@ -491,7 +490,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender }); const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender });
await assertTokensToSendCalled( 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 ...@@ -501,7 +500,7 @@ function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, ope
const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }); const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator });
await assertTokensToSendCalled( 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,
); );
}); });
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { BN, constants, expectEvent, expectRevert, singletons } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert, singletons } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
...@@ -19,10 +17,10 @@ const { ...@@ -19,10 +17,10 @@ const {
shouldBehaveLikeERC20Approve, shouldBehaveLikeERC20Approve,
} = require('../ERC20/ERC20.behavior'); } = require('../ERC20/ERC20.behavior');
const ERC777 = contract.fromArtifact('ERC777Mock'); const ERC777 = artifacts.require('ERC777Mock');
const ERC777SenderRecipientMock = contract.fromArtifact('ERC777SenderRecipientMock'); const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock');
describe('ERC777', function () { contract('ERC777', function (accounts) {
const [ registryFunder, holder, defaultOperatorA, defaultOperatorB, newOperator, anyone ] = accounts; const [ registryFunder, holder, defaultOperatorA, defaultOperatorB, newOperator, anyone ] = accounts;
const initialSupply = new BN('10000'); const initialSupply = new BN('10000');
...@@ -53,7 +51,7 @@ describe('ERC777', function () { ...@@ -53,7 +51,7 @@ describe('ERC777', function () {
describe('when the owner is the zero address', function () { describe('when the owner is the zero address', function () {
it('reverts', async function () { it('reverts', async function () {
await expectRevert(this.token.approveInternal(ZERO_ADDRESS, anyone, initialSupply), 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 () { ...@@ -182,13 +180,13 @@ describe('ERC777', function () {
it('reverts when self-authorizing', async function () { it('reverts when self-authorizing', async function () {
await expectRevert( 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 () { it('reverts when self-revoking', async function () {
await expectRevert( 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 () { ...@@ -252,7 +250,7 @@ describe('ERC777', function () {
it('cannot be revoked for themselves', async function () { it('cannot be revoked for themselves', async function () {
await expectRevert( await expectRevert(
this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA }), this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA }),
'ERC777: revoking self as operator' 'ERC777: revoking self as operator',
); );
}); });
......
const { accounts, contract, web3 } = require('@openzeppelin/test-environment');
const { balance, ether, expectRevert, send, expectEvent } = require('@openzeppelin/test-helpers'); const { balance, ether, expectRevert, send, expectEvent } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const AddressImpl = contract.fromArtifact('AddressImpl'); const AddressImpl = artifacts.require('AddressImpl');
const EtherReceiver = contract.fromArtifact('EtherReceiverMock'); const EtherReceiver = artifacts.require('EtherReceiverMock');
const CallReceiverMock = contract.fromArtifact('CallReceiverMock'); const CallReceiverMock = artifacts.require('CallReceiverMock');
describe('Address', function () { contract('Address', function (accounts) {
const [ recipient, other ] = accounts; const [ recipient, other ] = accounts;
beforeEach(async function () { beforeEach(async function () {
...@@ -85,7 +83,7 @@ describe('Address', function () { ...@@ -85,7 +83,7 @@ describe('Address', function () {
await this.contractRecipient.setAcceptEther(false); await this.contractRecipient.setAcceptEther(false);
await expectRevert( await expectRevert(
this.mock.sendValue(this.contractRecipient.address, funds), 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 () { ...@@ -120,7 +118,7 @@ describe('Address', function () {
await expectRevert( await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed' 'Address: low-level call failed',
); );
}); });
...@@ -133,7 +131,7 @@ describe('Address', function () { ...@@ -133,7 +131,7 @@ describe('Address', function () {
await expectRevert( await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'CallReceiverMock: reverting' 'CallReceiverMock: reverting',
); );
}); });
...@@ -145,10 +143,10 @@ describe('Address', function () { ...@@ -145,10 +143,10 @@ describe('Address', function () {
}, []); }, []);
await expectRevert( await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '90000' }),
'Address: low-level call failed' 'Address: low-level call failed',
); );
}).timeout(5000); });
it('reverts when the called function throws', async function () { it('reverts when the called function throws', async function () {
const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ const abiEncodedCall = web3.eth.abi.encodeFunctionCall({
...@@ -159,7 +157,7 @@ describe('Address', function () { ...@@ -159,7 +157,7 @@ describe('Address', function () {
await expectRevert( await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed' 'Address: low-level call failed',
); );
}); });
...@@ -172,7 +170,7 @@ describe('Address', function () { ...@@ -172,7 +170,7 @@ describe('Address', function () {
await expectRevert( await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed' 'Address: low-level call failed',
); );
}); });
}); });
...@@ -222,7 +220,7 @@ describe('Address', function () { ...@@ -222,7 +220,7 @@ describe('Address', function () {
await expectRevert( await expectRevert(
this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount), 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 () { ...@@ -255,7 +253,7 @@ describe('Address', function () {
expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0'); expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0');
const receipt = await this.mock.functionCallWithValue( 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); expect(await tracker.delta()).to.be.bignumber.equal(amount);
...@@ -274,9 +272,112 @@ describe('Address', function () { ...@@ -274,9 +272,112 @@ describe('Address', function () {
await send.ether(other, this.mock.address, amount); await send.ether(other, this.mock.address, amount);
await expectRevert( await expectRevert(
this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount), 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',
);
});
});
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment