Commit 8c521fff by Francisco Giordano

Merge upstream openzeppelin-contracts into upstream-patched

parents 289964c6 c2c08af1
<!-- 0. 🎉 Thank you for submitting a PR! --> <!-- Thank you for your interest in contributing to OpenZeppelin! -->
<!-- 1. Does this close any open issues? Please list them below. --> <!-- Consider opening an issue for discussion prior to submitting a PR. -->
<!-- New features will be merged faster if they were first discussed and designed with the team. -->
<!-- Keep in mind that new features have a better chance of being merged fast if Fixes #???? <!-- Fill in with issue number -->
they were first discussed and designed with the maintainers. If there is no
corresponding issue, please consider opening one for discussion first! -->
Fixes # <!-- Describe the changes introduced in this pull request. -->
<!-- Include any context necessary for understanding the PR's purpose. -->
<!-- 2. Describe the changes introduced in this pull request. -->
<!-- Include any context necessary for understanding the PR's purpose. -->
<!-- 3. Before submitting, please make sure that you have: #### PR Checklist
- reviewed the OpenZeppelin Contributor Guidelines
(https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md), <!-- Before merging the pull request all of the following must be complete. -->
- added tests where applicable to test new functionality, <!-- Feel free to submit a PR or Draft PR even if some items are pending. -->
- made sure that your contracts are well-documented, <!-- Some of the items may not apply. -->
- run the Solidity linter (`npm run lint:sol`) and fixed any issues,
- run the JS linter and fixed any issues (`npm run lint:fix`), and - [ ] Tests
- updated the changelog, if applicable. - [ ] Documentation
--> - [ ] Changelog entry
...@@ -12,7 +12,7 @@ jobs: ...@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v2
with: with:
node-version: 10.x node-version: 10.x
- uses: actions/cache@v2 - uses: actions/cache@v2
......
...@@ -25,6 +25,13 @@ ...@@ -25,6 +25,13 @@
* `ERC20Permit`: added an implementation of the ERC20 permit extension for gasless token approvals. ([#2237](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237)) * `ERC20Permit`: added an implementation of the ERC20 permit extension for gasless token approvals. ([#2237](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237))
* Presets: added token presets with preminted fixed supply `ERC20PresetFixedSupply` and `ERC777PresetFixedSupply`. ([#2399](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2399)) * Presets: added token presets with preminted fixed supply `ERC20PresetFixedSupply` and `ERC777PresetFixedSupply`. ([#2399](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2399))
* `Address`: added `functionDelegateCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333)) * `Address`: added `functionDelegateCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333))
* `Clones`: added a library for deploying EIP 1167 minimal proxies. ([#2449](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2449))
* `Context`: moved from `contracts/GSN` to `contracts/utils`. ([#2453](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2453))
* `PaymentSplitter`: replace usage of `.transfer()` with `Address.sendValue` for improved compatibility with smart wallets. ([#2455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2455))
* `UpgradeableProxy`: bubble revert reasons from initialization calls. ([#2454](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2454))
* `SafeMath`: fix a memory allocation issue by adding new `SafeMath.tryOp(uint,uint)→(bool,uint)` functions. `SafeMath.op(uint,uint,string)→uint` are now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462))
* `EnumerableMap`: fix a memory allocation issue by adding new `EnumerableMap.tryGet(uint)→(bool,address)` functions. `EnumerableMap.get(uint)→string` is now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462))
* `ERC165Checker`: added batch `getSupportedInterfaces`. ([#2469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2469))
## 3.3.0 (2020-11-26) ## 3.3.0 (2020-11-26)
......
...@@ -2,23 +2,4 @@ ...@@ -2,23 +2,4 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
/* import "../utils/Context.sol";
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
import "./IRelayRecipient.sol"; import "./IRelayRecipient.sol";
import "./IRelayHub.sol"; import "./IRelayHub.sol";
import "./Context.sol";
/** /**
* @dev Base GSN recipient contract: includes the {IRelayRecipient} interface * @dev Base GSN recipient contract: includes the {IRelayRecipient} interface
......
...@@ -4,7 +4,7 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -4,7 +4,7 @@ pragma solidity >=0.6.0 <0.8.0;
import "../utils/EnumerableSet.sol"; import "../utils/EnumerableSet.sol";
import "../utils/Address.sol"; import "../utils/Address.sol";
import "../GSN/Context.sol"; import "../utils/Context.sol";
/** /**
* @dev Contract module that allows children to implement role-based access * @dev Contract module that allows children to implement role-based access
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
/** /**
* @dev Contract module which provides a basic access control mechanism, where * @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to * there is an account (an owner) that can be granted exclusive access to
......
...@@ -18,6 +18,8 @@ import "./AccessControl.sol"; ...@@ -18,6 +18,8 @@ import "./AccessControl.sol";
* is in charge of proposing (resp executing) operations. A common use case is * is in charge of proposing (resp executing) operations. A common use case is
* to position this {TimelockController} as the owner of a smart contract, with * to position this {TimelockController} as the owner of a smart contract, with
* a multisig or a DAO as the sole proposer. * a multisig or a DAO as the sole proposer.
*
* _Available since v3.3._
*/ */
contract TimelockController is AccessControl { contract TimelockController is AccessControl {
......
...@@ -35,7 +35,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -35,7 +35,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
/** /**
* @dev See {IERC20Permit-permit}. * @dev See {IERC20Permit-permit}.
*/ */
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override { function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
// solhint-disable-next-line not-rely-on-time // solhint-disable-next-line not-rely-on-time
require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
...@@ -44,7 +44,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -44,7 +44,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
_PERMIT_TYPEHASH, _PERMIT_TYPEHASH,
owner, owner,
spender, spender,
amount, value,
_nonces[owner].current(), _nonces[owner].current(),
deadline deadline
) )
...@@ -56,7 +56,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -56,7 +56,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
require(signer == owner, "ERC20Permit: invalid signature"); require(signer == owner, "ERC20Permit: invalid signature");
_nonces[owner].increment(); _nonces[owner].increment();
_approve(owner, spender, amount); _approve(owner, spender, value);
} }
/** /**
......
...@@ -12,7 +12,7 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -12,7 +12,7 @@ pragma solidity >=0.6.0 <0.8.0;
*/ */
interface IERC20Permit { interface IERC20Permit {
/** /**
* @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval. * given `owner`'s signed approval.
* *
* IMPORTANT: The same issues {IERC20-approve} has related to transaction * IMPORTANT: The same issues {IERC20-approve} has related to transaction
...@@ -32,7 +32,7 @@ interface IERC20Permit { ...@@ -32,7 +32,7 @@ interface IERC20Permit {
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section]. * section].
*/ */
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/** /**
* @dev Returns the current nonce for `owner`. This value must be * @dev Returns the current nonce for `owner`. This value must be
......
...@@ -41,6 +41,29 @@ library ERC165Checker { ...@@ -41,6 +41,29 @@ library ERC165Checker {
} }
/** /**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in * @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically. * `interfaceIds`. Support for {IERC165} itself is queried automatically.
* *
......
...@@ -17,6 +17,52 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -17,6 +17,52 @@ pragma solidity >=0.6.0 <0.8.0;
*/ */
library SafeMath { library SafeMath {
/** /**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on * @dev Returns the addition of two unsigned integers, reverting on
* overflow. * overflow.
* *
...@@ -29,7 +75,6 @@ library SafeMath { ...@@ -29,7 +75,6 @@ library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) { function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b; uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow"); require(c >= a, "SafeMath: addition overflow");
return c; return c;
} }
...@@ -44,24 +89,8 @@ library SafeMath { ...@@ -44,24 +89,8 @@ library SafeMath {
* - Subtraction cannot overflow. * - Subtraction cannot overflow.
*/ */
function sub(uint256 a, uint256 b) internal pure returns (uint256) { function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow"); require(b <= a, "SafeMath: subtraction overflow");
} return a - b;
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
} }
/** /**
...@@ -75,21 +104,14 @@ library SafeMath { ...@@ -75,21 +104,14 @@ library SafeMath {
* - Multiplication cannot overflow. * - Multiplication cannot overflow.
*/ */
function mul(uint256 a, uint256 b) internal pure returns (uint256) { function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the if (a == 0) return 0;
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b; uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow"); require(c / a == b, "SafeMath: multiplication overflow");
return c; return c;
} }
/** /**
* @dev Returns the integer division of two unsigned integers. Reverts on * @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero. * division by zero. The result is rounded towards zero.
* *
* Counterpart to Solidity's `/` operator. Note: this function uses a * Counterpart to Solidity's `/` operator. Note: this function uses a
...@@ -101,48 +123,71 @@ library SafeMath { ...@@ -101,48 +123,71 @@ library SafeMath {
* - The divisor cannot be zero. * - The divisor cannot be zero.
*/ */
function div(uint256 a, uint256 b) internal pure returns (uint256) { function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero"); require(b > 0, "SafeMath: division by zero");
return a / b;
} }
/** /**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* division by zero. The result is rounded towards zero. * reverting when dividing by zero.
* *
* Counterpart to Solidity's `/` operator. Note: this function uses a * Counterpart to Solidity's `%` operator. This function uses a `revert`
* `revert` opcode (which leaves remaining gas untouched) while Solidity * opcode (which leaves remaining gas untouched) while Solidity uses an
* uses an invalid opcode to revert (consuming all remaining gas). * invalid opcode to revert (consuming all remaining gas).
* *
* Requirements: * Requirements:
* *
* - The divisor cannot be zero. * - The divisor cannot be zero.
*/ */
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, errorMessage); require(b > 0, "SafeMath: modulo by zero");
uint256 c = a / b; return a % b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold }
return c; /**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
} }
/** /**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * @dev Returns the integer division of two unsigned integers, reverting with custom message on
* Reverts when dividing by zero. * division by zero. The result is rounded towards zero.
* *
* Counterpart to Solidity's `%` operator. This function uses a `revert` * CAUTION: This function is deprecated because it requires allocating memory for the error
* opcode (which leaves remaining gas untouched) while Solidity uses an * message unnecessarily. For custom revert reasons use {tryDiv}.
* invalid opcode to revert (consuming all remaining gas). *
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
* *
* Requirements: * Requirements:
* *
* - The divisor cannot be zero. * - The divisor cannot be zero.
*/ */
function mod(uint256 a, uint256 b) internal pure returns (uint256) { function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero"); require(b > 0, errorMessage);
return a / b;
} }
/** /**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero. * reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
* *
* Counterpart to Solidity's `%` operator. This function uses a `revert` * Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an * opcode (which leaves remaining gas untouched) while Solidity uses an
...@@ -153,7 +198,7 @@ library SafeMath { ...@@ -153,7 +198,7 @@ library SafeMath {
* - The divisor cannot be zero. * - The divisor cannot be zero.
*/ */
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage); require(b > 0, errorMessage);
return a % b; return a % b;
} }
} }
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Clones.sol";
import "../utils/Address.sol";
contract ClonesMock {
using Address for address;
using Clones for address;
event NewInstance(address instance);
function clone(address master, bytes calldata initdata) public payable {
_initAndEmit(master.clone(), initdata);
}
function cloneDeterministic(address master, bytes32 salt, bytes calldata initdata) public payable {
_initAndEmit(master.cloneDeterministic(salt), initdata);
}
function predictDeterministicAddress(address master, bytes32 salt) public view returns (address predicted) {
return master.predictDeterministicAddress(salt);
}
function _initAndEmit(address instance, bytes memory initdata) private {
if (initdata.length > 0) {
instance.functionCallWithValue(initdata, msg.value);
}
emit NewInstance(instance);
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
contract ContextMock is Context { contract ContextMock is Context {
event Sender(address sender); event Sender(address sender);
......
...@@ -18,4 +18,8 @@ contract ERC165CheckerMock { ...@@ -18,4 +18,8 @@ contract ERC165CheckerMock {
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) { function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) {
return account.supportsAllInterfaces(interfaceIds); return account.supportsAllInterfaces(interfaceIds);
} }
function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) {
return account.getSupportedInterfaces(interfaceIds);
}
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../token/ERC777/ERC777.sol"; import "../token/ERC777/ERC777.sol";
contract ERC777Mock is Context, ERC777 { contract ERC777Mock is Context, ERC777 {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../token/ERC777/IERC777.sol"; import "../token/ERC777/IERC777.sol";
import "../token/ERC777/IERC777Sender.sol"; import "../token/ERC777/IERC777Sender.sol";
import "../token/ERC777/IERC777Recipient.sol"; import "../token/ERC777/IERC777Recipient.sol";
......
...@@ -34,7 +34,15 @@ contract EnumerableMapMock { ...@@ -34,7 +34,15 @@ contract EnumerableMapMock {
} }
function tryGet(uint256 key) public view returns (bool, address) {
return _map.tryGet(key);
}
function get(uint256 key) public view returns (address) { function get(uint256 key) public view returns (address) {
return _map.get(key); return _map.get(key);
} }
function getWithMessage(uint256 key, string calldata errorMessage) public view returns (address) {
return _map.get(key, errorMessage);
}
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
contract ReentrancyAttack is Context { contract ReentrancyAttack is Context {
function callSender(bytes4 data) public { function callSender(bytes4 data) public {
// solhint-disable-next-line avoid-low-level-calls // solhint-disable-next-line avoid-low-level-calls
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../token/ERC20/IERC20.sol"; import "../token/ERC20/IERC20.sol";
import "../token/ERC20/SafeERC20.sol"; import "../token/ERC20/SafeERC20.sol";
......
...@@ -5,23 +5,101 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -5,23 +5,101 @@ pragma solidity >=0.6.0 <0.8.0;
import "../math/SafeMath.sol"; import "../math/SafeMath.sol";
contract SafeMathMock { contract SafeMathMock {
function mul(uint256 a, uint256 b) public pure returns (uint256) { function tryAdd(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) {
return SafeMath.mul(a, b); return SafeMath.tryAdd(a, b);
} }
function div(uint256 a, uint256 b) public pure returns (uint256) { function trySub(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) {
return SafeMath.div(a, b); return SafeMath.trySub(a, b);
} }
function sub(uint256 a, uint256 b) public pure returns (uint256) { function tryMul(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) {
return SafeMath.sub(a, b); return SafeMath.tryMul(a, b);
}
function tryDiv(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) {
return SafeMath.tryDiv(a, b);
}
function tryMod(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) {
return SafeMath.tryMod(a, b);
} }
function add(uint256 a, uint256 b) public pure returns (uint256) { function add(uint256 a, uint256 b) public pure returns (uint256) {
return SafeMath.add(a, b); return SafeMath.add(a, b);
} }
function sub(uint256 a, uint256 b) public pure returns (uint256) {
return SafeMath.sub(a, b);
}
function mul(uint256 a, uint256 b) public pure returns (uint256) {
return SafeMath.mul(a, b);
}
function div(uint256 a, uint256 b) public pure returns (uint256) {
return SafeMath.div(a, b);
}
function mod(uint256 a, uint256 b) public pure returns (uint256) { function mod(uint256 a, uint256 b) public pure returns (uint256) {
return SafeMath.mod(a, b); return SafeMath.mod(a, b);
} }
function subWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) {
return SafeMath.sub(a, b, errorMessage);
}
function divWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) {
return SafeMath.div(a, b, errorMessage);
}
function modWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) {
return SafeMath.mod(a, b, errorMessage);
}
function addMemoryCheck() public pure returns (uint256 mem) {
uint256 length = 32;
// solhint-disable-next-line no-inline-assembly
assembly { mem := mload(0x40) }
for (uint256 i = 0; i < length; ++i) { SafeMath.add(1, 1); }
// solhint-disable-next-line no-inline-assembly
assembly { mem := sub(mload(0x40), mem) }
}
function subMemoryCheck() public pure returns (uint256 mem) {
uint256 length = 32;
// solhint-disable-next-line no-inline-assembly
assembly { mem := mload(0x40) }
for (uint256 i = 0; i < length; ++i) { SafeMath.sub(1, 1); }
// solhint-disable-next-line no-inline-assembly
assembly { mem := sub(mload(0x40), mem) }
}
function mulMemoryCheck() public pure returns (uint256 mem) {
uint256 length = 32;
// solhint-disable-next-line no-inline-assembly
assembly { mem := mload(0x40) }
for (uint256 i = 0; i < length; ++i) { SafeMath.mul(1, 1); }
// solhint-disable-next-line no-inline-assembly
assembly { mem := sub(mload(0x40), mem) }
}
function divMemoryCheck() public pure returns (uint256 mem) {
uint256 length = 32;
// solhint-disable-next-line no-inline-assembly
assembly { mem := mload(0x40) }
for (uint256 i = 0; i < length; ++i) { SafeMath.div(1, 1); }
// solhint-disable-next-line no-inline-assembly
assembly { mem := sub(mload(0x40), mem) }
}
function modMemoryCheck() public pure returns (uint256 mem) {
uint256 length = 32;
// solhint-disable-next-line no-inline-assembly
assembly { mem := mload(0x40) }
for (uint256 i = 0; i < length; ++i) { SafeMath.mod(1, 1); }
// solhint-disable-next-line no-inline-assembly
assembly { mem := sub(mload(0x40), mem) }
}
} }
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../math/SafeMath.sol"; import "../math/SafeMath.sol";
import "../utils/Address.sol";
/** /**
* @title PaymentSplitter * @title PaymentSplitter
...@@ -112,7 +113,7 @@ contract PaymentSplitter is Context { ...@@ -112,7 +113,7 @@ contract PaymentSplitter is Context {
_released[account] = _released[account].add(payment); _released[account] = _released[account].add(payment);
_totalReleased = _totalReleased.add(payment); _totalReleased = _totalReleased.add(payment);
account.transfer(payment); Address.sendValue(account, payment);
emit PaymentReleased(account, payment); emit PaymentReleased(account, payment);
} }
......
...@@ -36,7 +36,7 @@ contract Escrow is Ownable { ...@@ -36,7 +36,7 @@ contract Escrow is Ownable {
* @dev Stores the sent amount as credit to be withdrawn. * @dev Stores the sent amount as credit to be withdrawn.
* @param payee The destination address of the funds. * @param payee The destination address of the funds.
*/ */
function deposit(address payee) public virtual payable onlyOwner { function deposit(address payee) public payable virtual onlyOwner {
uint256 amount = msg.value; uint256 amount = msg.value;
_deposits[payee] = _deposits[payee].add(amount); _deposits[payee] = _deposits[payee].add(amount);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../access/AccessControl.sol"; import "../access/AccessControl.sol";
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../token/ERC1155/ERC1155.sol"; import "../token/ERC1155/ERC1155.sol";
import "../token/ERC1155/ERC1155Burnable.sol"; import "../token/ERC1155/ERC1155Burnable.sol";
import "../token/ERC1155/ERC1155Pausable.sol"; import "../token/ERC1155/ERC1155Pausable.sol";
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../access/AccessControl.sol"; import "../access/AccessControl.sol";
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../token/ERC20/ERC20.sol"; import "../token/ERC20/ERC20.sol";
import "../token/ERC20/ERC20Burnable.sol"; import "../token/ERC20/ERC20Burnable.sol";
import "../token/ERC20/ERC20Pausable.sol"; import "../token/ERC20/ERC20Pausable.sol";
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../access/AccessControl.sol"; import "../access/AccessControl.sol";
import "../GSN/Context.sol"; import "../utils/Context.sol";
import "../utils/Counters.sol"; import "../utils/Counters.sol";
import "../token/ERC721/ERC721.sol"; import "../token/ERC721/ERC721.sol";
import "../token/ERC721/ERC721Burnable.sol"; import "../token/ERC721/ERC721Burnable.sol";
......
...@@ -56,14 +56,14 @@ contract BeaconProxy is Proxy { ...@@ -56,14 +56,14 @@ contract BeaconProxy is Proxy {
/** /**
* @dev Changes the proxy to use a new beacon. * @dev Changes the proxy to use a new beacon.
* *
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
* *
* Requirements: * Requirements:
* *
* - `beacon` must be a contract. * - `beacon` must be a contract.
* - The implementation returned by `beacon` must be a contract. * - The implementation returned by `beacon` must be a contract.
*/ */
function _setBeacon(address beacon, bytes memory data) internal { function _setBeacon(address beacon, bytes memory data) internal virtual {
require( require(
Address.isContract(beacon), Address.isContract(beacon),
"BeaconProxy: beacon is not a contract" "BeaconProxy: beacon is not a contract"
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address master) internal returns (address instance) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `master` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
return predictDeterministicAddress(master, salt, address(this));
}
}
...@@ -3,16 +3,17 @@ ...@@ -3,16 +3,17 @@
// solhint-disable-next-line compiler-version // solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0; pragma solidity >=0.4.24 <0.8.0;
import "../utils/Address.sol";
/** /**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
* *
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}. * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
* *
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/ */
...@@ -49,15 +50,6 @@ abstract contract Initializable { ...@@ -49,15 +50,6 @@ abstract contract Initializable {
/// @dev Returns true if and only if the function is running in the constructor /// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) { function _isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and return !Address.isContract(address(this));
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
// solhint-disable-next-line no-inline-assembly
assembly { cs := extcodesize(self) }
return cs == 0;
} }
} }
...@@ -6,19 +6,19 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -6,19 +6,19 @@ pragma solidity >=0.6.0 <0.8.0;
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function. * be specified by overriding the virtual {_implementation} function.
* *
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function. * different contract through the {_delegate} function.
* *
* The success and return data of the delegated call will be returned back to the caller of the proxy. * The success and return data of the delegated call will be returned back to the caller of the proxy.
*/ */
abstract contract Proxy { abstract contract Proxy {
/** /**
* @dev Delegates the current call to `implementation`. * @dev Delegates the current call to `implementation`.
* *
* This function does not return to its internall call site, it will return directly to the external caller. * This function does not return to its internall call site, it will return directly to the external caller.
*/ */
function _delegate(address implementation) internal { function _delegate(address implementation) internal virtual {
// solhint-disable-next-line no-inline-assembly // solhint-disable-next-line no-inline-assembly
assembly { assembly {
// Copy msg.data. We take full control of memory in this inline assembly // Copy msg.data. We take full control of memory in this inline assembly
...@@ -48,10 +48,10 @@ abstract contract Proxy { ...@@ -48,10 +48,10 @@ abstract contract Proxy {
/** /**
* @dev Delegates the current call to the address returned by `_implementation()`. * @dev Delegates the current call to the address returned by `_implementation()`.
* *
* This function does not return to its internall call site, it will return directly to the external caller. * This function does not return to its internall call site, it will return directly to the external caller.
*/ */
function _fallback() internal { function _fallback() internal virtual {
_beforeFallback(); _beforeFallback();
_delegate(_implementation()); _delegate(_implementation());
} }
...@@ -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 () external payable { fallback () external payable virtual {
_fallback(); _fallback();
} }
...@@ -68,14 +68,14 @@ abstract contract Proxy { ...@@ -68,14 +68,14 @@ 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 () external payable { receive () external payable virtual {
_fallback(); _fallback();
} }
/** /**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions. * call, or as part of the Solidity `fallback` or `receive` functions.
* *
* If overriden should call `super._beforeFallback()`. * If overriden should call `super._beforeFallback()`.
*/ */
function _beforeFallback() internal virtual { function _beforeFallback() internal virtual {
......
...@@ -13,9 +13,9 @@ contract ProxyAdmin is Ownable { ...@@ -13,9 +13,9 @@ contract ProxyAdmin is Ownable {
/** /**
* @dev Returns the current implementation of `proxy`. * @dev Returns the current implementation of `proxy`.
* *
* Requirements: * Requirements:
* *
* - This contract must be the admin of `proxy`. * - This contract must be the admin of `proxy`.
*/ */
function getProxyImplementation(TransparentUpgradeableProxy proxy) public view returns (address) { function getProxyImplementation(TransparentUpgradeableProxy proxy) public view returns (address) {
...@@ -28,9 +28,9 @@ contract ProxyAdmin is Ownable { ...@@ -28,9 +28,9 @@ contract ProxyAdmin is Ownable {
/** /**
* @dev Returns the current admin of `proxy`. * @dev Returns the current admin of `proxy`.
* *
* Requirements: * Requirements:
* *
* - This contract must be the admin of `proxy`. * - This contract must be the admin of `proxy`.
*/ */
function getProxyAdmin(TransparentUpgradeableProxy proxy) public view returns (address) { function getProxyAdmin(TransparentUpgradeableProxy proxy) public view returns (address) {
...@@ -43,35 +43,35 @@ contract ProxyAdmin is Ownable { ...@@ -43,35 +43,35 @@ contract ProxyAdmin is Ownable {
/** /**
* @dev Changes the admin of `proxy` to `newAdmin`. * @dev Changes the admin of `proxy` to `newAdmin`.
* *
* Requirements: * Requirements:
* *
* - This contract must be the current admin of `proxy`. * - This contract must be the current admin of `proxy`.
*/ */
function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public onlyOwner { function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
proxy.changeAdmin(newAdmin); proxy.changeAdmin(newAdmin);
} }
/** /**
* @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
* *
* Requirements: * Requirements:
* *
* - This contract must be the admin of `proxy`. * - This contract must be the admin of `proxy`.
*/ */
function upgrade(TransparentUpgradeableProxy proxy, address implementation) public onlyOwner { function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
proxy.upgradeTo(implementation); proxy.upgradeTo(implementation);
} }
/** /**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
* {TransparentUpgradeableProxy-upgradeToAndCall}. * {TransparentUpgradeableProxy-upgradeToAndCall}.
* *
* Requirements: * Requirements:
* *
* - This contract must be the admin of `proxy`. * - This contract must be the admin of `proxy`.
*/ */
function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable onlyOwner { function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data); proxy.upgradeToAndCall{value: msg.value}(implementation, data);
} }
} }
...@@ -3,13 +3,15 @@ ...@@ -3,13 +3,15 @@
[.readme-notice] [.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy
This is a low-level set of contracts implementing the proxy pattern for upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page.
The abstract {Proxy} contract implements the core delegation functionality. If the concrete proxies that we provide below are not suitable, we encourage building on top of this base contract since it contains an assembly block that may be hard to get right. The abstract {Proxy} contract implements the core delegation functionality. If the concrete proxies that we provide below are not suitable, we encourage building on top of this base contract since it contains an assembly block that may be hard to get right.
Upgradeability is implemented in the {UpgradeableProxy} contract, although it provides only an internal upgrade interface. For an upgrade interface exposed externally to an admin, we provide {TransparentUpgradeableProxy}. Both of these contracts use the storage slots specified in https://eips.ethereum.org/EIPS/eip-1967[EIP1967] to avoid clashes with the storage of the implementation contract behind the proxy. Upgradeability is implemented in the {UpgradeableProxy} contract, although it provides only an internal upgrade interface. For an upgrade interface exposed externally to an admin, we provide {TransparentUpgradeableProxy}. Both of these contracts use the storage slots specified in https://eips.ethereum.org/EIPS/eip-1967[EIP1967] to avoid clashes with the storage of the implementation contract behind the proxy.
An alternative upgradeability mechanism is provided in <<UpgradeableBeacon>>. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. In this pattern, the proxy contract doesn't hold the implementation address in storage like {UpgradeableProxy}, but the address of a {UpgradeableBeacon} contract, which is where the implementation address is actually stored and retrieved from. The `upgrade` operations that change the implementation contract address are then sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. An alternative upgradeability mechanism is provided in <<Beacon>>. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. In this pattern, the proxy contract doesn't hold the implementation address in storage like {UpgradeableProxy}, but the address of a {UpgradeableBeacon} contract, which is where the implementation address is actually stored and retrieved from. The `upgrade` operations that change the implementation contract address are then sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded.
The {Clones} library provides a way to deploy minimal non-upgradeable proxies for cheap. This can be useful for applications that require deploying many instances of the same contract (for example one per user, or one per task). These instances are designed to be both cheap to deploy, and cheap to call. The drawback being that they are not upgradeable.
CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Buidler. CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Buidler.
...@@ -21,7 +23,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th ...@@ -21,7 +23,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
{{TransparentUpgradeableProxy}} {{TransparentUpgradeableProxy}}
== UpgradeableBeacon == Beacon
{{BeaconProxy}} {{BeaconProxy}}
...@@ -29,6 +31,10 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th ...@@ -29,6 +31,10 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
{{UpgradeableBeacon}} {{UpgradeableBeacon}}
== Minimal Clones
{{Clones}}
== Utilities == Utilities
{{Initializable}} {{Initializable}}
......
...@@ -6,22 +6,22 @@ import "./UpgradeableProxy.sol"; ...@@ -6,22 +6,22 @@ import "./UpgradeableProxy.sol";
/** /**
* @dev This contract implements a proxy that is upgradeable by an admin. * @dev This contract implements a proxy that is upgradeable by an admin.
* *
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the * clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand: * things that go hand in hand:
* *
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches one of the admin functions exposed by the proxy itself. * that call matches one of the admin functions exposed by the proxy itself.
* 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
* implementation. If the admin tries to call a function on the implementation it will fail with an error that says * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
* "admin cannot fallback to proxy target". * "admin cannot fallback to proxy target".
* *
* These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
* the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
* 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 interface of your proxy. * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*/ */
...@@ -60,9 +60,9 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -60,9 +60,9 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
* @dev Returns the current admin. * @dev Returns the current admin.
* *
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
* *
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
...@@ -73,9 +73,9 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -73,9 +73,9 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
* @dev Returns the current implementation. * @dev Returns the current implementation.
* *
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
* *
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
...@@ -86,12 +86,12 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -86,12 +86,12 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
* @dev Changes the admin of the proxy. * @dev Changes the admin of the proxy.
* *
* Emits an {AdminChanged} event. * Emits an {AdminChanged} event.
* *
* NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
*/ */
function changeAdmin(address newAdmin) external ifAdmin { function changeAdmin(address newAdmin) external virtual ifAdmin {
require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address"); require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
emit AdminChanged(_admin(), newAdmin); emit AdminChanged(_admin(), newAdmin);
_setAdmin(newAdmin); _setAdmin(newAdmin);
...@@ -99,10 +99,10 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -99,10 +99,10 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
* @dev Upgrade the implementation of the proxy. * @dev Upgrade the implementation of the proxy.
* *
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
*/ */
function upgradeTo(address newImplementation) external ifAdmin { function upgradeTo(address newImplementation) external virtual ifAdmin {
_upgradeTo(newImplementation); _upgradeTo(newImplementation);
} }
...@@ -110,14 +110,12 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -110,14 +110,12 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
* @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
* by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
* proxied contract. * proxied contract.
* *
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
*/ */
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { function upgradeToAndCall(address newImplementation, bytes calldata data) external payable virtual ifAdmin {
_upgradeTo(newImplementation); _upgradeTo(newImplementation);
// solhint-disable-next-line avoid-low-level-calls Address.functionDelegateCall(newImplementation, data);
(bool success,) = newImplementation.delegatecall(data);
require(success);
} }
/** /**
...@@ -146,7 +144,7 @@ contract TransparentUpgradeableProxy is UpgradeableProxy { ...@@ -146,7 +144,7 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
/** /**
* @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
*/ */
function _beforeFallback() internal override virtual { function _beforeFallback() internal virtual override {
require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
super._beforeFallback(); super._beforeFallback();
} }
......
...@@ -45,7 +45,7 @@ contract UpgradeableBeacon is IBeacon, Ownable { ...@@ -45,7 +45,7 @@ contract UpgradeableBeacon is IBeacon, Ownable {
* - msg.sender must be the owner of the contract. * - msg.sender must be the owner of the contract.
* - `newImplementation` must be a contract. * - `newImplementation` must be a contract.
*/ */
function upgradeTo(address newImplementation) public onlyOwner { function upgradeTo(address newImplementation) public virtual onlyOwner {
_setImplementation(newImplementation); _setImplementation(newImplementation);
emit Upgraded(newImplementation); emit Upgraded(newImplementation);
} }
......
...@@ -10,14 +10,14 @@ import "../utils/Address.sol"; ...@@ -10,14 +10,14 @@ import "../utils/Address.sol";
* implementation address that can be changed. This address is stored in storage in the location specified by * implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy. * implementation behind the proxy.
* *
* Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
* {TransparentUpgradeableProxy}. * {TransparentUpgradeableProxy}.
*/ */
contract UpgradeableProxy is Proxy { contract UpgradeableProxy is Proxy {
/** /**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
* *
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializating the storage of the proxy like a Solidity constructor. * function call, and allows initializating the storage of the proxy like a Solidity constructor.
*/ */
...@@ -25,9 +25,7 @@ contract UpgradeableProxy is Proxy { ...@@ -25,9 +25,7 @@ contract UpgradeableProxy is Proxy {
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_setImplementation(_logic); _setImplementation(_logic);
if(_data.length > 0) { if(_data.length > 0) {
// solhint-disable-next-line avoid-low-level-calls Address.functionDelegateCall(_logic, _data);
(bool success,) = _logic.delegatecall(_data);
require(success);
} }
} }
...@@ -46,7 +44,7 @@ contract UpgradeableProxy is Proxy { ...@@ -46,7 +44,7 @@ contract UpgradeableProxy is Proxy {
/** /**
* @dev Returns the current implementation address. * @dev Returns the current implementation address.
*/ */
function _implementation() internal override view returns (address impl) { function _implementation() internal view override returns (address impl) {
bytes32 slot = _IMPLEMENTATION_SLOT; bytes32 slot = _IMPLEMENTATION_SLOT;
// solhint-disable-next-line no-inline-assembly // solhint-disable-next-line no-inline-assembly
assembly { assembly {
...@@ -56,10 +54,10 @@ contract UpgradeableProxy is Proxy { ...@@ -56,10 +54,10 @@ contract UpgradeableProxy is Proxy {
/** /**
* @dev Upgrades the proxy to a new implementation. * @dev Upgrades the proxy to a new implementation.
* *
* Emits an {Upgraded} event. * Emits an {Upgraded} event.
*/ */
function _upgradeTo(address newImplementation) internal { function _upgradeTo(address newImplementation) internal virtual {
_setImplementation(newImplementation); _setImplementation(newImplementation);
emit Upgraded(newImplementation); emit Upgraded(newImplementation);
} }
......
...@@ -5,7 +5,7 @@ pragma solidity >=0.6.0 <0.8.0; ...@@ -5,7 +5,7 @@ pragma solidity >=0.6.0 <0.8.0;
import "./IERC1155.sol"; import "./IERC1155.sol";
import "./IERC1155MetadataURI.sol"; import "./IERC1155MetadataURI.sol";
import "./IERC1155Receiver.sol"; import "./IERC1155Receiver.sol";
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "../../introspection/ERC165.sol"; import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
import "../../utils/Address.sol"; import "../../utils/Address.sol";
...@@ -355,7 +355,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { ...@@ -355,7 +355,8 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256[] memory amounts, uint256[] memory amounts,
bytes memory data bytes memory data
) )
internal virtual internal
virtual
{ } { }
function _doSafeTransferAcceptanceCheck( function _doSafeTransferAcceptanceCheck(
......
...@@ -30,7 +30,9 @@ abstract contract ERC1155Pausable is ERC1155, Pausable { ...@@ -30,7 +30,9 @@ abstract contract ERC1155Pausable is ERC1155, Pausable {
uint256[] memory amounts, uint256[] memory amounts,
bytes memory data bytes memory data
) )
internal virtual override internal
virtual
override
{ {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data); super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "./IERC20.sol"; import "./IERC20.sol";
import "../../math/SafeMath.sol"; import "../../math/SafeMath.sol";
...@@ -284,7 +284,7 @@ contract ERC20 is Context, IERC20 { ...@@ -284,7 +284,7 @@ contract ERC20 is Context, IERC20 {
* applications that interact with token contracts will not expect * applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does. * {decimals} to ever change, and may work incorrectly if it does.
*/ */
function _setupDecimals(uint8 decimals_) internal { function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_; _decimals = decimals_;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "./ERC20.sol"; import "./ERC20.sol";
/** /**
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "./IERC721.sol"; import "./IERC721.sol";
import "./IERC721Metadata.sol"; import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol"; import "./IERC721Enumerable.sol";
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "./ERC721.sol"; import "./ERC721.sol";
/** /**
......
...@@ -117,8 +117,8 @@ interface IERC721 is IERC165 { ...@@ -117,8 +117,8 @@ interface IERC721 is IERC165 {
* *
* Requirements: * Requirements:
* *
* - `from` cannot be the zero address. * - `from` cannot be the zero address.
* - `to` cannot be the zero address. * - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`. * - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../../GSN/Context.sol"; import "../../utils/Context.sol";
import "./IERC777.sol"; import "./IERC777.sol";
import "./IERC777Recipient.sol"; import "./IERC777Recipient.sol";
import "./IERC777Sender.sol"; import "./IERC777Sender.sol";
...@@ -70,7 +70,9 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -70,7 +70,9 @@ contract ERC777 is Context, IERC777, IERC20 {
string memory name_, string memory name_,
string memory symbol_, string memory symbol_,
address[] memory defaultOperators_ address[] memory defaultOperators_
) public { )
public
{
_name = name_; _name = name_;
_symbol = symbol_; _symbol = symbol_;
...@@ -136,7 +138,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -136,7 +138,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Also emits a {IERC20-Transfer} event for ERC20 compatibility. * Also emits a {IERC20-Transfer} event for ERC20 compatibility.
*/ */
function send(address recipient, uint256 amount, bytes memory data) public override { function send(address recipient, uint256 amount, bytes memory data) public virtual override {
_send(_msgSender(), recipient, amount, data, "", true); _send(_msgSender(), recipient, amount, data, "", true);
} }
...@@ -148,7 +150,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -148,7 +150,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Also emits a {Sent} event. * Also emits a {Sent} event.
*/ */
function transfer(address recipient, uint256 amount) public override returns (bool) { function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address"); require(recipient != address(0), "ERC777: transfer to the zero address");
address from = _msgSender(); address from = _msgSender();
...@@ -167,17 +169,14 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -167,17 +169,14 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Also emits a {IERC20-Transfer} event for ERC20 compatibility. * Also emits a {IERC20-Transfer} event for ERC20 compatibility.
*/ */
function burn(uint256 amount, bytes memory data) public override { function burn(uint256 amount, bytes memory data) public virtual override {
_burn(_msgSender(), amount, data, ""); _burn(_msgSender(), amount, data, "");
} }
/** /**
* @dev See {IERC777-isOperatorFor}. * @dev See {IERC777-isOperatorFor}.
*/ */
function isOperatorFor( function isOperatorFor(address operator, address tokenHolder) public view override returns (bool) {
address operator,
address tokenHolder
) public view override returns (bool) {
return operator == tokenHolder || return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator]; _operators[tokenHolder][operator];
...@@ -186,7 +185,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -186,7 +185,7 @@ contract ERC777 is Context, IERC777, IERC20 {
/** /**
* @dev See {IERC777-authorizeOperator}. * @dev See {IERC777-authorizeOperator}.
*/ */
function authorizeOperator(address operator) public override { function authorizeOperator(address operator) public virtual override {
require(_msgSender() != operator, "ERC777: authorizing self as operator"); require(_msgSender() != operator, "ERC777: authorizing self as operator");
if (_defaultOperators[operator]) { if (_defaultOperators[operator]) {
...@@ -201,7 +200,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -201,7 +200,7 @@ contract ERC777 is Context, IERC777, IERC20 {
/** /**
* @dev See {IERC777-revokeOperator}. * @dev See {IERC777-revokeOperator}.
*/ */
function revokeOperator(address operator) public override { function revokeOperator(address operator) public virtual override {
require(operator != _msgSender(), "ERC777: revoking self as operator"); require(operator != _msgSender(), "ERC777: revoking self as operator");
if (_defaultOperators[operator]) { if (_defaultOperators[operator]) {
...@@ -232,7 +231,9 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -232,7 +231,9 @@ contract ERC777 is Context, IERC777, IERC20 {
bytes memory data, bytes memory data,
bytes memory operatorData bytes memory operatorData
) )
public override public
virtual
override
{ {
require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder");
_send(sender, recipient, amount, data, operatorData, true); _send(sender, recipient, amount, data, operatorData, true);
...@@ -243,7 +244,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -243,7 +244,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Emits {Burned} and {IERC20-Transfer} events. * Emits {Burned} and {IERC20-Transfer} events.
*/ */
function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public override { function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public virtual override {
require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder");
_burn(account, amount, data, operatorData); _burn(account, amount, data, operatorData);
} }
...@@ -264,7 +265,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -264,7 +265,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Note that accounts cannot have allowance issued by their operators. * Note that accounts cannot have allowance issued by their operators.
*/ */
function approve(address spender, uint256 value) public override returns (bool) { function approve(address spender, uint256 value) public virtual override returns (bool) {
address holder = _msgSender(); address holder = _msgSender();
_approve(holder, spender, value); _approve(holder, spender, value);
return true; return true;
...@@ -279,7 +280,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -279,7 +280,7 @@ contract ERC777 is Context, IERC777, IERC20 {
* *
* Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events.
*/ */
function transferFrom(address holder, address recipient, uint256 amount) public override returns (bool) { function transferFrom(address holder, address recipient, uint256 amount) public virtual override returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address"); require(recipient != address(0), "ERC777: transfer to the zero address");
require(holder != address(0), "ERC777: transfer from the zero address"); require(holder != address(0), "ERC777: transfer from the zero address");
...@@ -318,7 +319,8 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -318,7 +319,8 @@ contract ERC777 is Context, IERC777, IERC20 {
bytes memory userData, bytes memory userData,
bytes memory operatorData bytes memory operatorData
) )
internal virtual internal
virtual
{ {
require(account != address(0), "ERC777: mint to the zero address"); require(account != address(0), "ERC777: mint to the zero address");
...@@ -354,6 +356,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -354,6 +356,7 @@ contract ERC777 is Context, IERC777, IERC20 {
bool requireReceptionAck bool requireReceptionAck
) )
internal internal
virtual
{ {
require(from != address(0), "ERC777: send from the zero address"); require(from != address(0), "ERC777: send from the zero address");
require(to != address(0), "ERC777: send to the zero address"); require(to != address(0), "ERC777: send to the zero address");
...@@ -380,7 +383,8 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -380,7 +383,8 @@ contract ERC777 is Context, IERC777, IERC20 {
bytes memory data, bytes memory data,
bytes memory operatorData bytes memory operatorData
) )
internal virtual internal
virtual
{ {
require(from != address(0), "ERC777: burn from the zero address"); require(from != address(0), "ERC777: burn from the zero address");
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
...@@ -144,6 +144,16 @@ library EnumerableMap { ...@@ -144,6 +144,16 @@ library EnumerableMap {
} }
/** /**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
}
/**
* @dev Returns the value associated with `key`. O(1). * @dev Returns the value associated with `key`. O(1).
* *
* Requirements: * Requirements:
...@@ -151,11 +161,16 @@ library EnumerableMap { ...@@ -151,11 +161,16 @@ library EnumerableMap {
* - `key` must be in the map. * - `key` must be in the map.
*/ */
function _get(Map storage map, bytes32 key) private view returns (bytes32) { function _get(Map storage map, bytes32 key) private view returns (bytes32) {
return _get(map, key, "EnumerableMap: nonexistent key"); uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
} }
/** /**
* @dev Same as {_get}, with a custom error message when `key` is not in the map. * @dev Same as {_get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/ */
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) { function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key]; uint256 keyIndex = map._indexes[key];
...@@ -218,6 +233,15 @@ library EnumerableMap { ...@@ -218,6 +233,15 @@ library EnumerableMap {
} }
/** /**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1). * @dev Returns the value associated with `key`. O(1).
* *
* Requirements: * Requirements:
...@@ -230,6 +254,9 @@ library EnumerableMap { ...@@ -230,6 +254,9 @@ library EnumerableMap {
/** /**
* @dev Same as {get}, with a custom error message when `key` is not in the map. * @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/ */
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) { function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;
import "../GSN/Context.sol"; import "./Context.sol";
/** /**
* @dev Contract module which allows children to implement an emergency stop * @dev Contract module which allows children to implement an emergency stop
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -15,6 +15,6 @@ solidity-docgen \ ...@@ -15,6 +15,6 @@ solidity-docgen \
-o "$OUTDIR" \ -o "$OUTDIR" \
-e contracts/mocks,contracts/examples \ -e contracts/mocks,contracts/examples \
--output-structure readmes \ --output-structure readmes \
--solc-module scripts/prepare-docs-solc.js --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"
...@@ -37,6 +37,12 @@ contract('ERC165Checker', function (accounts) { ...@@ -37,6 +37,12 @@ contract('ERC165Checker', function (accounts) {
const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
expect(supported).to.equal(false); expect(supported).to.equal(false);
}); });
it('does not support mock interface via getSupportedInterfaces', async function () {
const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
expect(supported.length).to.equal(1);
expect(supported[0]).to.equal(false);
});
}); });
context('ERC165 supported', function () { context('ERC165 supported', function () {
...@@ -58,6 +64,12 @@ contract('ERC165Checker', function (accounts) { ...@@ -58,6 +64,12 @@ contract('ERC165Checker', function (accounts) {
const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
expect(supported).to.equal(false); expect(supported).to.equal(false);
}); });
it('does not support mock interface via getSupportedInterfaces', async function () {
const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
expect(supported.length).to.equal(1);
expect(supported[0]).to.equal(false);
});
}); });
context('ERC165 and single interface supported', function () { context('ERC165 and single interface supported', function () {
...@@ -79,6 +91,12 @@ contract('ERC165Checker', function (accounts) { ...@@ -79,6 +91,12 @@ contract('ERC165Checker', function (accounts) {
const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
expect(supported).to.equal(true); expect(supported).to.equal(true);
}); });
it('supports mock interface via getSupportedInterfaces', async function () {
const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]);
expect(supported.length).to.equal(1);
expect(supported[0]).to.equal(true);
});
}); });
context('ERC165 and many interfaces supported', function () { context('ERC165 and many interfaces supported', function () {
...@@ -117,6 +135,34 @@ contract('ERC165Checker', function (accounts) { ...@@ -117,6 +135,34 @@ contract('ERC165Checker', function (accounts) {
const supported = await this.mock.supportsAllInterfaces(this.target.address, interfaceIdsToTest); const supported = await this.mock.supportsAllInterfaces(this.target.address, interfaceIdsToTest);
expect(supported).to.equal(false); expect(supported).to.equal(false);
}); });
it('supports all interfaceIds via getSupportedInterfaces', async function () {
const supported = await this.mock.getSupportedInterfaces(this.target.address, this.supportedInterfaces);
expect(supported.length).to.equal(3);
expect(supported[0]).to.equal(true);
expect(supported[1]).to.equal(true);
expect(supported[2]).to.equal(true);
});
it('supports none of the interfaces queried via getSupportedInterfaces', async function () {
const interfaceIdsToTest = [DUMMY_UNSUPPORTED_ID, DUMMY_UNSUPPORTED_ID_2];
const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest);
expect(supported.length).to.equal(2);
expect(supported[0]).to.equal(false);
expect(supported[1]).to.equal(false);
});
it('supports not all of the interfaces queried via getSupportedInterfaces', async function () {
const interfaceIdsToTest = [...this.supportedInterfaces, DUMMY_UNSUPPORTED_ID];
const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest);
expect(supported.length).to.equal(4);
expect(supported[0]).to.equal(true);
expect(supported[1]).to.equal(true);
expect(supported[2]).to.equal(true);
expect(supported[3]).to.equal(false);
});
}); });
context('account address does not support ERC165', function () { context('account address does not support ERC165', function () {
...@@ -134,5 +180,11 @@ contract('ERC165Checker', function (accounts) { ...@@ -134,5 +180,11 @@ contract('ERC165Checker', function (accounts) {
const supported = await this.mock.supportsAllInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]); const supported = await this.mock.supportsAllInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]);
expect(supported).to.equal(false); expect(supported).to.equal(false);
}); });
it('does not support mock interface via getSupportedInterfaces', async function () {
const supported = await this.mock.getSupportedInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]);
expect(supported.length).to.equal(1);
expect(supported[0]).to.equal(false);
});
}); });
}); });
...@@ -5,142 +5,394 @@ const { expect } = require('chai'); ...@@ -5,142 +5,394 @@ const { expect } = require('chai');
const SafeMathMock = artifacts.require('SafeMathMock'); const SafeMathMock = artifacts.require('SafeMathMock');
function expectStruct (value, expected) {
for (const key in expected) {
if (BN.isBN(value[key])) {
expect(value[key]).to.be.bignumber.equal(expected[key]);
} else {
expect(value[key]).to.be.equal(expected[key]);
}
}
}
contract('SafeMath', function (accounts) { contract('SafeMath', function (accounts) {
beforeEach(async function () { beforeEach(async function () {
this.safeMath = await SafeMathMock.new(); this.safeMath = await SafeMathMock.new();
}); });
async function testCommutative (fn, lhs, rhs, expected) { async function testCommutative (fn, lhs, rhs, expected, ...extra) {
expect(await fn(lhs, rhs)).to.be.bignumber.equal(expected); expect(await fn(lhs, rhs, ...extra)).to.be.bignumber.equal(expected);
expect(await fn(rhs, lhs)).to.be.bignumber.equal(expected); expect(await fn(rhs, lhs, ...extra)).to.be.bignumber.equal(expected);
} }
async function testFailsCommutative (fn, lhs, rhs, reason) { async function testFailsCommutative (fn, lhs, rhs, reason, ...extra) {
await expectRevert(fn(lhs, rhs), reason); await expectRevert(fn(lhs, rhs, ...extra), reason);
await expectRevert(fn(rhs, lhs), reason); await expectRevert(fn(rhs, lhs, ...extra), reason);
} }
describe('add', function () { async function testCommutativeIterable (fn, lhs, rhs, expected, ...extra) {
it('adds correctly', async function () { expectStruct(await fn(lhs, rhs, ...extra), expected);
const a = new BN('5678'); expectStruct(await fn(rhs, lhs, ...extra), expected);
const b = new BN('1234'); }
await testCommutative(this.safeMath.add, a, b, a.add(b)); describe('with flag', function () {
}); describe('add', function () {
it('adds correctly', async function () {
const a = new BN('5678');
const b = new BN('1234');
it('reverts on addition overflow', async function () { testCommutativeIterable(this.safeMath.tryAdd, a, b, { flag: true, value: a.add(b) });
const a = MAX_UINT256; });
const b = new BN('1');
it('reverts on addition overflow', async function () {
const a = MAX_UINT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.add, a, b, 'SafeMath: addition overflow'); testCommutativeIterable(this.safeMath.tryAdd, a, b, { flag: false, value: '0' });
});
}); });
});
describe('sub', function () { describe('sub', function () {
it('subtracts correctly', async function () { it('subtracts correctly', async function () {
const a = new BN('5678'); const a = new BN('5678');
const b = new BN('1234'); const b = new BN('1234');
expect(await this.safeMath.sub(a, b)).to.be.bignumber.equal(a.sub(b)); expectStruct(await this.safeMath.trySub(a, b), { flag: true, value: a.sub(b) });
}); });
it('reverts if subtraction result would be negative', async function () { it('reverts if subtraction result would be negative', async function () {
const a = new BN('1234'); const a = new BN('1234');
const b = new BN('5678'); const b = new BN('5678');
await expectRevert(this.safeMath.sub(a, b), 'SafeMath: subtraction overflow'); expectStruct(await this.safeMath.trySub(a, b), { flag: false, value: '0' });
});
}); });
});
describe('mul', function () { describe('mul', function () {
it('multiplies correctly', async function () { it('multiplies correctly', async function () {
const a = new BN('1234'); const a = new BN('1234');
const b = new BN('5678'); const b = new BN('5678');
testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: true, value: a.mul(b) });
});
it('multiplies by zero correctly', async function () {
const a = new BN('0');
const b = new BN('5678');
testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: true, value: a.mul(b) });
});
await testCommutative(this.safeMath.mul, a, b, a.mul(b)); it('reverts on multiplication overflow', async function () {
const a = MAX_UINT256;
const b = new BN('2');
testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: false, value: '0' });
});
}); });
it('multiplies by zero correctly', async function () { describe('div', function () {
const a = new BN('0'); it('divides correctly', async function () {
const b = new BN('5678'); const a = new BN('5678');
const b = new BN('5678');
await testCommutative(this.safeMath.mul, a, b, '0'); expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) });
});
it('divides zero correctly', async function () {
const a = new BN('0');
const b = new BN('5678');
expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) });
});
it('returns complete number result on non-even division', async function () {
const a = new BN('7000');
const b = new BN('5678');
expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) });
});
it('reverts on division by zero', async function () {
const a = new BN('5678');
const b = new BN('0');
expectStruct(await this.safeMath.tryDiv(a, b), { flag: false, value: '0' });
});
}); });
it('reverts on multiplication overflow', async function () { describe('mod', function () {
const a = MAX_UINT256; describe('modulos correctly', async function () {
const b = new BN('2'); it('when the dividend is smaller than the divisor', async function () {
const a = new BN('284');
const b = new BN('5678');
expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) });
});
it('when the dividend is equal to the divisor', async function () {
const a = new BN('5678');
const b = new BN('5678');
expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) });
});
await testFailsCommutative(this.safeMath.mul, a, b, 'SafeMath: multiplication overflow'); it('when the dividend is larger than the divisor', async function () {
const a = new BN('7000');
const b = new BN('5678');
expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) });
});
it('when the dividend is a multiple of the divisor', async function () {
const a = new BN('17034'); // 17034 == 5678 * 3
const b = new BN('5678');
expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) });
});
});
it('reverts with a 0 divisor', async function () {
const a = new BN('5678');
const b = new BN('0');
expectStruct(await this.safeMath.tryMod(a, b), { flag: false, value: '0' });
});
}); });
}); });
describe('div', function () { describe('with default revert message', function () {
it('divides correctly', async function () { describe('add', function () {
const a = new BN('5678'); it('adds correctly', async function () {
const b = new BN('5678'); const a = new BN('5678');
const b = new BN('1234');
await testCommutative(this.safeMath.add, a, b, a.add(b));
});
it('reverts on addition overflow', async function () {
const a = MAX_UINT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.add, a, b, 'SafeMath: addition overflow');
});
});
describe('sub', function () {
it('subtracts correctly', async function () {
const a = new BN('5678');
const b = new BN('1234');
expect(await this.safeMath.sub(a, b)).to.be.bignumber.equal(a.sub(b));
});
it('reverts if subtraction result would be negative', async function () {
const a = new BN('1234');
const b = new BN('5678');
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal(a.div(b)); await expectRevert(this.safeMath.sub(a, b), 'SafeMath: subtraction overflow');
});
}); });
it('divides zero correctly', async function () { describe('mul', function () {
const a = new BN('0'); it('multiplies correctly', async function () {
const b = new BN('5678'); const a = new BN('1234');
const b = new BN('5678');
await testCommutative(this.safeMath.mul, a, b, a.mul(b));
});
it('multiplies by zero correctly', async function () {
const a = new BN('0');
const b = new BN('5678');
await testCommutative(this.safeMath.mul, a, b, '0');
});
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('0'); it('reverts on multiplication overflow', async function () {
const a = MAX_UINT256;
const b = new BN('2');
await testFailsCommutative(this.safeMath.mul, a, b, 'SafeMath: multiplication overflow');
});
}); });
it('returns complete number result on non-even division', async function () { describe('div', function () {
const a = new BN('7000'); it('divides correctly', async function () {
const b = new BN('5678'); const a = new BN('5678');
const b = new BN('5678');
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal(a.div(b));
});
it('divides zero correctly', async function () {
const a = new BN('0');
const b = new BN('5678');
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('0');
});
it('returns complete number result on non-even division', async function () {
const a = new BN('7000');
const b = new BN('5678');
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('1');
});
it('reverts on division by zero', async function () {
const a = new BN('5678');
const b = new BN('0');
expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('1'); await expectRevert(this.safeMath.div(a, b), 'SafeMath: division by zero');
});
}); });
it('reverts on division by zero', async function () { describe('mod', function () {
const a = new BN('5678'); describe('modulos correctly', async function () {
const b = new BN('0'); it('when the dividend is smaller than the divisor', async function () {
const a = new BN('284');
const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b));
});
it('when the dividend is equal to the divisor', async function () {
const a = new BN('5678');
const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b));
});
await expectRevert(this.safeMath.div(a, b), 'SafeMath: division by zero'); it('when the dividend is larger than the divisor', async function () {
const a = new BN('7000');
const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b));
});
it('when the dividend is a multiple of the divisor', async function () {
const a = new BN('17034'); // 17034 == 5678 * 3
const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b));
});
});
it('reverts with a 0 divisor', async function () {
const a = new BN('5678');
const b = new BN('0');
await expectRevert(this.safeMath.mod(a, b), 'SafeMath: modulo by zero');
});
}); });
}); });
describe('mod', function () { describe('with custom revert message', function () {
describe('modulos correctly', async function () { describe('sub', function () {
it('when the dividend is smaller than the divisor', async function () { it('subtracts correctly', async function () {
const a = new BN('284'); const a = new BN('5678');
const b = new BN('1234');
expect(await this.safeMath.subWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.sub(b));
});
it('reverts if subtraction result would be negative', async function () {
const a = new BN('1234');
const b = new BN('5678'); const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b)); await expectRevert(this.safeMath.subWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage');
}); });
});
it('when the dividend is equal to the divisor', async function () { describe('div', function () {
it('divides correctly', async function () {
const a = new BN('5678'); const a = new BN('5678');
const b = new BN('5678'); const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b)); expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.div(b));
}); });
it('when the dividend is larger than the divisor', async function () { it('divides zero correctly', async function () {
const a = new BN('7000'); const a = new BN('0');
const b = new BN('5678'); const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b)); expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal('0');
}); });
it('when the dividend is a multiple of the divisor', async function () { it('returns complete number result on non-even division', async function () {
const a = new BN('17034'); // 17034 == 5678 * 3 const a = new BN('7000');
const b = new BN('5678'); const b = new BN('5678');
expect(await this.safeMath.mod(a, b)).to.be.bignumber.equal(a.mod(b)); expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal('1');
});
it('reverts on division by zero', async function () {
const a = new BN('5678');
const b = new BN('0');
await expectRevert(this.safeMath.divWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage');
}); });
}); });
it('reverts with a 0 divisor', async function () { describe('mod', function () {
const a = new BN('5678'); describe('modulos correctly', async function () {
const b = new BN('0'); it('when the dividend is smaller than the divisor', async function () {
const a = new BN('284');
const b = new BN('5678');
expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b));
});
it('when the dividend is equal to the divisor', async function () {
const a = new BN('5678');
const b = new BN('5678');
expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b));
});
it('when the dividend is larger than the divisor', async function () {
const a = new BN('7000');
const b = new BN('5678');
expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b));
});
it('when the dividend is a multiple of the divisor', async function () {
const a = new BN('17034'); // 17034 == 5678 * 3
const b = new BN('5678');
expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b));
});
});
it('reverts with a 0 divisor', async function () {
const a = new BN('5678');
const b = new BN('0');
await expectRevert(this.safeMath.modWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage');
});
});
});
describe('memory leakage', function () {
it('add', async function () {
expect(await this.safeMath.addMemoryCheck()).to.be.bignumber.equal('0');
});
it('sub', async function () {
expect(await this.safeMath.subMemoryCheck()).to.be.bignumber.equal('0');
});
it('mul', async function () {
expect(await this.safeMath.mulMemoryCheck()).to.be.bignumber.equal('0');
});
it('div', async function () {
expect(await this.safeMath.divMemoryCheck()).to.be.bignumber.equal('0');
});
await expectRevert(this.safeMath.mod(a, b), 'SafeMath: modulo by zero'); it('mod', async function () {
expect(await this.safeMath.modMemoryCheck()).to.be.bignumber.equal('0');
}); });
}); });
}); });
const { expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const DummyImplementation = artifacts.require('DummyImplementation');
module.exports = function shouldBehaveLikeClone (createClone) {
before('deploy implementation', async function () {
this.implementation = web3.utils.toChecksumAddress((await DummyImplementation.new()).address);
});
const assertProxyInitialization = function ({ value, balance }) {
it('initializes the proxy', async function () {
const dummy = new DummyImplementation(this.proxy);
expect(await dummy.value()).to.be.bignumber.equal(value.toString());
});
it('has expected balance', async function () {
expect(await web3.eth.getBalance(this.proxy)).to.be.bignumber.equal(balance.toString());
});
};
describe('initialization without parameters', function () {
describe('non payable', function () {
const expectedInitializedValue = 10;
const initializeData = new DummyImplementation('').contract.methods['initializeNonPayable()']().encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData)
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
it('reverts', async function () {
await expectRevert.unspecified(
createClone(this.implementation, initializeData, { value }),
);
});
});
});
describe('payable', function () {
const expectedInitializedValue = 100;
const initializeData = new DummyImplementation('').contract.methods['initializePayable()']().encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData)
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData, { value })
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: value,
});
});
});
});
describe('initialization with parameters', function () {
describe('non payable', function () {
const expectedInitializedValue = 10;
const initializeData = new DummyImplementation('').contract
.methods.initializeNonPayableWithValue(expectedInitializedValue).encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData)
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
it('reverts', async function () {
await expectRevert.unspecified(
createClone(this.implementation, initializeData, { value }),
);
});
});
});
describe('payable', function () {
const expectedInitializedValue = 42;
const initializeData = new DummyImplementation('').contract
.methods.initializePayableWithValue(expectedInitializedValue).encodeABI();
describe('when not sending balance', function () {
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData)
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: 0,
});
});
describe('when sending some balance', function () {
const value = 10e5;
beforeEach('creating proxy', async function () {
this.proxy = (
await createClone(this.implementation, initializeData, { value })
).address;
});
assertProxyInitialization({
value: expectedInitializedValue,
balance: value,
});
});
});
});
};
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const shouldBehaveLikeClone = require('./Clones.behaviour');
const ClonesMock = artifacts.require('ClonesMock');
contract('Clones', function (accounts) {
describe('clone', function () {
shouldBehaveLikeClone(async (implementation, initData, opts = {}) => {
const factory = await ClonesMock.new();
const receipt = await factory.clone(implementation, initData, { value: opts.value });
const address = receipt.logs.find(({ event }) => event === 'NewInstance').args.instance;
return { address };
});
});
describe('cloneDeterministic', function () {
shouldBehaveLikeClone(async (implementation, initData, opts = {}) => {
const salt = web3.utils.randomHex(32);
const factory = await ClonesMock.new();
const receipt = await factory.cloneDeterministic(implementation, salt, initData, { value: opts.value });
const address = receipt.logs.find(({ event }) => event === 'NewInstance').args.instance;
return { address };
});
it('address already used', async function () {
const implementation = web3.utils.randomHex(20);
const salt = web3.utils.randomHex(32);
const factory = await ClonesMock.new();
// deploy once
expectEvent(
await factory.cloneDeterministic(implementation, salt, '0x'),
'NewInstance',
);
// deploy twice
await expectRevert(
factory.cloneDeterministic(implementation, salt, '0x'),
'ERC1167: create2 failed',
);
});
it('address prediction', async function () {
const implementation = web3.utils.randomHex(20);
const salt = web3.utils.randomHex(32);
const factory = await ClonesMock.new();
const predicted = await factory.predictDeterministicAddress(implementation, salt);
expectEvent(
await factory.cloneDeterministic(implementation, salt, '0x'),
'NewInstance',
{ instance: predicted },
);
});
});
});
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 ethereumjsUtil = require('ethereumjs-util');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -19,6 +19,10 @@ const ClashingImplementation = artifacts.require('ClashingImplementation'); ...@@ -19,6 +19,10 @@ 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';
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
}
module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) { module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) {
const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts; const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts;
...@@ -308,13 +312,13 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro ...@@ -308,13 +312,13 @@ module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createPro
describe('storage', function () { describe('storage', function () {
it('should store the implementation address in specified location', async function () { it('should store the implementation address in specified location', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16); const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16);
const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot)); const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot));
expect(implementation).to.be.equal(this.implementationV0); expect(implementation).to.be.equal(this.implementationV0);
}); });
it('should store the admin proxy in specified location', async function () { it('should store the admin proxy in specified location', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(ADMIN_LABEL))).subn(1).toString(16); const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(ADMIN_LABEL))).subn(1).toString(16);
const proxyAdmin = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot)); const proxyAdmin = toChecksumAddress(await web3.eth.getStorageAt(this.proxyAddress, slot));
expect(proxyAdmin).to.be.equal(proxyAdminAddress); expect(proxyAdmin).to.be.equal(proxyAdminAddress);
}); });
......
const { BN, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, expectRevert } = require('@openzeppelin/test-helpers');
const { toChecksumAddress, keccak256 } = require('ethereumjs-util'); const ethereumjsUtil = require('ethereumjs-util');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -7,6 +7,10 @@ const DummyImplementation = artifacts.require('DummyImplementation'); ...@@ -7,6 +7,10 @@ const DummyImplementation = artifacts.require('DummyImplementation');
const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation'; const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
}
module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAdminAddress, proxyCreator) { module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAdminAddress, proxyCreator) {
it('cannot be initialized with a non-contract address', async function () { it('cannot be initialized with a non-contract address', async function () {
const nonContractAddress = proxyCreator; const nonContractAddress = proxyCreator;
...@@ -24,7 +28,7 @@ module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAd ...@@ -24,7 +28,7 @@ module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAd
const assertProxyInitialization = function ({ value, balance }) { const assertProxyInitialization = function ({ value, balance }) {
it('sets the implementation address', async function () { it('sets the implementation address', async function () {
const slot = '0x' + new BN(keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16); const slot = '0x' + new BN(ethereumjsUtil.keccak256(Buffer.from(IMPLEMENTATION_LABEL))).subn(1).toString(16);
const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxy, slot)); const implementation = toChecksumAddress(await web3.eth.getStorageAt(this.proxy, slot));
expect(implementation).to.be.equal(this.implementation); expect(implementation).to.be.equal(this.implementation);
}); });
...@@ -210,5 +214,17 @@ module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAd ...@@ -210,5 +214,17 @@ module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAd
}); });
}); });
}); });
describe('reverting initialization', function () {
const initializeData = new DummyImplementation('').contract
.methods.reverts().encodeABI();
it('reverts', async function () {
await expectRevert(
createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator }),
'DummyImplementation reverted',
);
});
});
}); });
}; };
const { BN, expectEvent } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const zip = require('lodash.zip'); const zip = require('lodash.zip');
...@@ -139,4 +139,43 @@ contract('EnumerableMap', function (accounts) { ...@@ -139,4 +139,43 @@ contract('EnumerableMap', function (accounts) {
expect(await this.map.contains(keyB)).to.equal(false); expect(await this.map.contains(keyB)).to.equal(false);
}); });
}); });
describe('read', function () {
beforeEach(async function () {
await this.map.set(keyA, accountA);
});
describe('get', function () {
it('existing value', async function () {
expect(await this.map.get(keyA)).to.be.equal(accountA);
});
it('missing value', async function () {
await expectRevert(this.map.get(keyB), 'EnumerableMap: nonexistent key');
});
});
describe('get with message', function () {
it('existing value', async function () {
expect(await this.map.getWithMessage(keyA, 'custom error string')).to.be.equal(accountA);
});
it('missing value', async function () {
await expectRevert(this.map.getWithMessage(keyB, 'custom error string'), 'custom error string');
});
});
describe('tryGet', function () {
it('existing value', async function () {
expect(await this.map.tryGet(keyA)).to.be.deep.equal({
0: true,
1: accountA,
});
});
it('missing value', async function () {
expect(await this.map.tryGet(keyB)).to.be.deep.equal({
0: false,
1: constants.ZERO_ADDRESS,
});
});
});
});
}); });
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