Commit 435f0650 by Francisco Giordano

Merge upstream openzeppelin-contracts into upstream-patched

parents 0fdc159c 7d20d0e2
......@@ -27,3 +27,8 @@ jobs:
- run: bash scripts/upgradeable/transpile.sh
if: github.event_name == 'pull_request'
- run: npm run test
env:
FORCE_COLOR: 1
ENABLE_GAS_REPORT: 1
- name: Print gas report
run: cat gas-report.txt
......@@ -20,6 +20,17 @@
## Unreleased
* Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin.
* `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492))
* `ERC20`: Removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
* `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504))
* `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518))
* `GSN`: Deprecate GSNv1 support in favor of upcomming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521))
* `ERC165`: Remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behaviour remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505))
* `Initializable`: Make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531))
## 3.4.0 (2021-02-02)
* `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411))
* `EIP712`: added helpers to verify EIP712 typed data signatures on chain. ([#2418](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2418))
* `ERC20Permit`: added an implementation of the ERC20 permit extension for gasless token approvals. ([#2237](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237))
......@@ -47,6 +58,12 @@ If you're using our implementation of ERC777 from version 3.3.0 or earlier, and
* `TimelockController`: added a contract to augment access control schemes with a delay. ([#2354](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2354))
* `EnumerableSet`: added `Bytes32Set`, for sets of `bytes32`. ([#2395](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2395))
## 3.2.2-solc-0.7 (2020-10-28)
* Resolve warnings introduced by Solidity 0.7.4. ([#2396](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2396))
## 3.2.1-solc-0.7 (2020-09-15)
* `ERC777`: Remove a warning about function state visibility in Solidity 0.7. ([#2327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2327))
## 3.2.0 (2020-09-10)
### New features
......
......@@ -34,7 +34,7 @@ The package replicates the structure of the main OpenZeppelin Contracts package,
Constructors are replaced by internal initializer functions following the naming convention `__{ContractName}_init`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend.
```diff
- constructor() ERC721("MyCollectible", "MCO") public {
- constructor() ERC721("MyCollectible", "MCO") {
+ function initialize() initializer public {
+ __ERC721_init("MyCollectible", "MCO");
}
......
const fs = require('fs');
const path = require('path');
usePlugin('solidity-coverage');
usePlugin('@nomiclabs/buidler-truffle5');
for (const f of fs.readdirSync(path.join(__dirname, 'buidler'))) {
require(path.join(__dirname, 'buidler', f));
}
module.exports = {
networks: {
buidlerevm: {
blockGasLimit: 10000000,
},
},
solc: {
version: '0.6.12',
},
};
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
import "./IRelayRecipient.sol";
import "./IRelayHub.sol";
/**
* @dev Base GSN recipient contract: includes the {IRelayRecipient} interface
* and enables GSN support on all contracts in the inheritance tree.
*
* TIP: This contract is abstract. The functions {IRelayRecipient-acceptRelayedCall},
* {_preRelayedCall}, and {_postRelayedCall} are not implemented and must be
* provided by derived contracts. See the
* xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategies] for more
* information on how to use the pre-built {GSNRecipientSignature} and
* {GSNRecipientERC20Fee}, or how to write your own.
*/
abstract contract GSNRecipient is IRelayRecipient, Context {
// Default RelayHub address, deployed on mainnet and all testnets at the same address
address private _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
uint256 constant private _RELAYED_CALL_ACCEPTED = 0;
uint256 constant private _RELAYED_CALL_REJECTED = 11;
// How much gas is forwarded to postRelayedCall
uint256 constant internal _POST_RELAYED_CALL_MAX_GAS = 100000;
/**
* @dev Emitted when a contract changes its {IRelayHub} contract to a new one.
*/
event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
/**
* @dev Returns the address of the {IRelayHub} contract for this recipient.
*/
function getHubAddr() public view virtual override returns (address) {
return _relayHub;
}
/**
* @dev Switches to a new {IRelayHub} instance. This method is added for future-proofing: there's no reason to not
* use the default instance.
*
* IMPORTANT: After upgrading, the {GSNRecipient} will no longer be able to receive relayed calls from the old
* {IRelayHub} instance. Additionally, all funds should be previously withdrawn via {_withdrawDeposits}.
*/
function _upgradeRelayHub(address newRelayHub) internal virtual {
address currentRelayHub = _relayHub;
require(newRelayHub != address(0), "GSNRecipient: new RelayHub is the zero address");
require(newRelayHub != currentRelayHub, "GSNRecipient: new RelayHub is the current one");
emit RelayHubChanged(currentRelayHub, newRelayHub);
_relayHub = newRelayHub;
}
/**
* @dev Returns the version string of the {IRelayHub} for which this recipient implementation was built. If
* {_upgradeRelayHub} is used, the new {IRelayHub} instance should be compatible with this version.
*/
// This function is view for future-proofing, it may require reading from
// storage in the future.
function relayHubVersion() public view virtual returns (string memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return "1.0.0";
}
/**
* @dev Withdraws the recipient's deposits in `RelayHub`.
*
* Derived contracts should expose this in an external interface with proper access control.
*/
function _withdrawDeposits(uint256 amount, address payable payee) internal virtual {
IRelayHub(getHubAddr()).withdraw(amount, payee);
}
// Overrides for Context's functions: when called from RelayHub, sender and
// data require some pre-processing: the actual sender is stored at the end
// of the call data, which in turns means it needs to be removed from it
// when handling said data.
/**
* @dev Replacement for msg.sender. Returns the actual sender of a transaction: msg.sender for regular transactions,
* and the end-user for GSN relayed calls (where msg.sender is actually `RelayHub`).
*
* IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.sender`, and use {_msgSender} instead.
*/
function _msgSender() internal view virtual override returns (address payable) {
if (msg.sender != getHubAddr()) {
return msg.sender;
} else {
return _getRelayedCallSender();
}
}
/**
* @dev Replacement for msg.data. Returns the actual calldata of a transaction: msg.data for regular transactions,
* and a reduced version for GSN relayed calls (where msg.data contains additional information).
*
* IMPORTANT: Contracts derived from {GSNRecipient} should never use `msg.data`, and use {_msgData} instead.
*/
function _msgData() internal view virtual override returns (bytes memory) {
if (msg.sender != getHubAddr()) {
return msg.data;
} else {
return _getRelayedCallData();
}
}
// Base implementations for pre and post relayedCall: only RelayHub can invoke them, and data is forwarded to the
// internal hook.
/**
* @dev See `IRelayRecipient.preRelayedCall`.
*
* This function should not be overridden directly, use `_preRelayedCall` instead.
*
* * Requirements:
*
* - the caller must be the `RelayHub` contract.
*/
function preRelayedCall(bytes memory context) public virtual override returns (bytes32) {
require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
return _preRelayedCall(context);
}
/**
* @dev See `IRelayRecipient.preRelayedCall`.
*
* Called by `GSNRecipient.preRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
* must implement this function with any relayed-call preprocessing they may wish to do.
*
*/
function _preRelayedCall(bytes memory context) internal virtual returns (bytes32);
/**
* @dev See `IRelayRecipient.postRelayedCall`.
*
* This function should not be overridden directly, use `_postRelayedCall` instead.
*
* * Requirements:
*
* - the caller must be the `RelayHub` contract.
*/
function postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) public virtual override {
require(msg.sender == getHubAddr(), "GSNRecipient: caller is not RelayHub");
_postRelayedCall(context, success, actualCharge, preRetVal);
}
/**
* @dev See `IRelayRecipient.postRelayedCall`.
*
* Called by `GSNRecipient.postRelayedCall`, which asserts the caller is the `RelayHub` contract. Derived contracts
* must implement this function with any relayed-call postprocessing they may wish to do.
*
*/
function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal virtual;
/**
* @dev Return this in acceptRelayedCall to proceed with the execution of a relayed call. Note that this contract
* will be charged a fee by RelayHub
*/
function _approveRelayedCall() internal pure virtual returns (uint256, bytes memory) {
return _approveRelayedCall("");
}
/**
* @dev See `GSNRecipient._approveRelayedCall`.
*
* This overload forwards `context` to _preRelayedCall and _postRelayedCall.
*/
function _approveRelayedCall(bytes memory context) internal pure virtual returns (uint256, bytes memory) {
return (_RELAYED_CALL_ACCEPTED, context);
}
/**
* @dev Return this in acceptRelayedCall to impede execution of a relayed call. No fees will be charged.
*/
function _rejectRelayedCall(uint256 errorCode) internal pure virtual returns (uint256, bytes memory) {
return (_RELAYED_CALL_REJECTED + errorCode, "");
}
/*
* @dev Calculates how much RelayHub will charge a recipient for using `gas` at a `gasPrice`, given a relayer's
* `serviceFee`.
*/
function _computeCharge(uint256 gas, uint256 gasPrice, uint256 serviceFee) internal pure virtual returns (uint256) {
// The fee is expressed as a percentage. E.g. a value of 40 stands for a 40% fee, so the recipient will be
// charged for 1.4 times the spent amount.
return (gas * gasPrice * (100 + serviceFee)) / 100;
}
function _getRelayedCallSender() private pure returns (address payable result) {
// We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
// is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
// so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
// require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
// bytes. This can always be done due to the 32-byte prefix.
// The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
// easiest/most-efficient way to perform this operation.
// These fields are not accessible from assembly
bytes memory array = msg.data;
uint256 index = msg.data.length;
// solhint-disable-next-line no-inline-assembly
assembly {
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return result;
}
function _getRelayedCallData() private pure returns (bytes memory) {
// RelayHub appends the sender address at the end of the calldata, so in order to retrieve the actual msg.data,
// we must strip the last 20 bytes (length of an address type) from it.
uint256 actualDataLength = msg.data.length - 20;
bytes memory actualData = new bytes(actualDataLength);
for (uint256 i = 0; i < actualDataLength; ++i) {
actualData[i] = msg.data[i];
}
return actualData;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./GSNRecipient.sol";
import "../math/SafeMath.sol";
import "../access/Ownable.sol";
import "../token/ERC20/SafeERC20.sol";
import "../token/ERC20/ERC20.sol";
/**
* @dev A xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategy] that charges transaction fees in a special purpose ERC20
* token, which we refer to as the gas payment token. The amount charged is exactly the amount of Ether charged to the
* recipient. This means that the token is essentially pegged to the value of Ether.
*
* The distribution strategy of the gas payment token to users is not defined by this contract. It's a mintable token
* whose only minter is the recipient, so the strategy must be implemented in a derived contract, making use of the
* internal {_mint} function.
*/
contract GSNRecipientERC20Fee is GSNRecipient {
using SafeERC20 for __unstable__ERC20Owned;
using SafeMath for uint256;
enum GSNRecipientERC20FeeErrorCodes {
INSUFFICIENT_BALANCE
}
__unstable__ERC20Owned private _token;
/**
* @dev The arguments to the constructor are the details that the gas payment token will have: `name` and `symbol`. `decimals` is hard-coded to 18.
*/
constructor(string memory name, string memory symbol) public {
_token = new __unstable__ERC20Owned(name, symbol);
}
/**
* @dev Returns the gas payment token.
*/
function token() public view virtual returns (__unstable__ERC20Owned) {
return _token;
}
/**
* @dev Internal function that mints the gas payment token. Derived contracts should expose this function in their public API, with proper access control mechanisms.
*/
function _mint(address account, uint256 amount) internal virtual {
token().mint(account, amount);
}
/**
* @dev Ensures that only users with enough gas payment token balance can have transactions relayed through the GSN.
*/
function acceptRelayedCall(
address,
address from,
bytes memory,
uint256 transactionFee,
uint256 gasPrice,
uint256,
uint256,
bytes memory,
uint256 maxPossibleCharge
)
public
view
virtual
override
returns (uint256, bytes memory)
{
if (token().balanceOf(from) < maxPossibleCharge) {
return _rejectRelayedCall(uint256(GSNRecipientERC20FeeErrorCodes.INSUFFICIENT_BALANCE));
}
return _approveRelayedCall(abi.encode(from, maxPossibleCharge, transactionFee, gasPrice));
}
/**
* @dev Implements the precharge to the user. The maximum possible charge (depending on gas limit, gas price, and
* fee) will be deducted from the user balance of gas payment token. Note that this is an overestimation of the
* actual charge, necessary because we cannot predict how much gas the execution will actually need. The remainder
* is returned to the user in {_postRelayedCall}.
*/
function _preRelayedCall(bytes memory context) internal virtual override returns (bytes32) {
(address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
// The maximum token charge is pre-charged from the user
token().safeTransferFrom(from, address(this), maxPossibleCharge);
return 0;
}
/**
* @dev Returns to the user the extra amount that was previously charged, once the actual execution cost is known.
*/
function _postRelayedCall(bytes memory context, bool, uint256 actualCharge, bytes32) internal virtual override {
(address from, uint256 maxPossibleCharge, uint256 transactionFee, uint256 gasPrice) =
abi.decode(context, (address, uint256, uint256, uint256));
// actualCharge is an _estimated_ charge, which assumes postRelayedCall will use all available gas.
// This implementation's gas cost can be roughly estimated as 10k gas, for the two SSTORE operations in an
// ERC20 transfer.
uint256 overestimation = _computeCharge(_POST_RELAYED_CALL_MAX_GAS.sub(10000), gasPrice, transactionFee);
actualCharge = actualCharge.sub(overestimation);
// After the relayed call has been executed and the actual charge estimated, the excess pre-charge is returned
token().safeTransfer(from, maxPossibleCharge.sub(actualCharge));
}
}
/**
* @title __unstable__ERC20Owned
* @dev An ERC20 token owned by another contract, which has minting permissions and can use transferFrom to receive
* anyone's tokens. This contract is an internal helper for GSNRecipientERC20Fee, and should not be used
* outside of this context.
*/
// solhint-disable-next-line contract-name-camelcase
contract __unstable__ERC20Owned is ERC20, Ownable {
uint256 private constant _UINT256_MAX = 2**256 - 1;
constructor(string memory name, string memory symbol) public ERC20(name, symbol) { }
// The owner (GSNRecipientERC20Fee) can mint tokens
function mint(address account, uint256 amount) public virtual onlyOwner {
_mint(account, amount);
}
// The owner has 'infinite' allowance for all token holders
function allowance(address tokenOwner, address spender) public view virtual override returns (uint256) {
if (spender == owner()) {
return _UINT256_MAX;
} else {
return super.allowance(tokenOwner, spender);
}
}
// Allowance for the owner cannot be changed (it is always 'infinite')
function _approve(address tokenOwner, address spender, uint256 value) internal virtual override {
if (spender == owner()) {
return;
} else {
super._approve(tokenOwner, spender, value);
}
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
if (recipient == owner()) {
_transfer(sender, recipient, amount);
return true;
} else {
return super.transferFrom(sender, recipient, amount);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./GSNRecipient.sol";
import "../cryptography/ECDSA.sol";
/**
* @dev A xref:ROOT:gsn-strategies.adoc#gsn-strategies[GSN strategy] that allows relayed transactions through when they are
* accompanied by the signature of a trusted signer. The intent is for this signature to be generated by a server that
* performs validations off-chain. Note that nothing is charged to the user in this scheme. Thus, the server should make
* sure to account for this in their economic and threat model.
*/
contract GSNRecipientSignature is GSNRecipient {
using ECDSA for bytes32;
address private _trustedSigner;
enum GSNRecipientSignatureErrorCodes {
INVALID_SIGNER
}
/**
* @dev Sets the trusted signer that is going to be producing signatures to approve relayed calls.
*/
constructor(address trustedSigner) public {
require(trustedSigner != address(0), "GSNRecipientSignature: trusted signer is the zero address");
_trustedSigner = trustedSigner;
}
/**
* @dev Ensures that only transactions with a trusted signature can be relayed through the GSN.
*/
function acceptRelayedCall(
address relay,
address from,
bytes memory encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes memory approvalData,
uint256
)
public
view
virtual
override
returns (uint256, bytes memory)
{
bytes memory blob = abi.encodePacked(
relay,
from,
encodedFunction,
transactionFee,
gasPrice,
gasLimit,
nonce, // Prevents replays on RelayHub
getHubAddr(), // Prevents replays in multiple RelayHubs
address(this) // Prevents replays in multiple recipients
);
if (keccak256(blob).toEthSignedMessageHash().recover(approvalData) == _trustedSigner) {
return _approveRelayedCall();
} else {
return _rejectRelayedCall(uint256(GSNRecipientSignatureErrorCodes.INVALID_SIGNER));
}
}
function _preRelayedCall(bytes memory) internal virtual override returns (bytes32) { }
function _postRelayedCall(bytes memory, bool, uint256, bytes32) internal virtual override { }
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface for `RelayHub`, the core contract of the GSN. Users should not need to interact with this contract
* directly.
*
* See the https://github.com/OpenZeppelin/openzeppelin-gsn-helpers[OpenZeppelin GSN helpers] for more information on
* how to deploy an instance of `RelayHub` on your local test network.
*/
interface IRelayHub {
// Relay management
/**
* @dev Adds stake to a relay and sets its `unstakeDelay`. If the relay does not exist, it is created, and the caller
* of this function becomes its owner. If the relay already exists, only the owner can call this function. A relay
* cannot be its own owner.
*
* All Ether in this function call will be added to the relay's stake.
* Its unstake delay will be assigned to `unstakeDelay`, but the new value must be greater or equal to the current one.
*
* Emits a {Staked} event.
*/
function stake(address relayaddr, uint256 unstakeDelay) external payable;
/**
* @dev Emitted when a relay's stake or unstakeDelay are increased
*/
event Staked(address indexed relay, uint256 stake, uint256 unstakeDelay);
/**
* @dev Registers the caller as a relay.
* The relay must be staked for, and not be a contract (i.e. this function must be called directly from an EOA).
*
* This function can be called multiple times, emitting new {RelayAdded} events. Note that the received
* `transactionFee` is not enforced by {relayCall}.
*
* Emits a {RelayAdded} event.
*/
function registerRelay(uint256 transactionFee, string calldata url) external;
/**
* @dev Emitted when a relay is registered or re-registered. Looking at these events (and filtering out
* {RelayRemoved} events) lets a client discover the list of available relays.
*/
event RelayAdded(address indexed relay, address indexed owner, uint256 transactionFee, uint256 stake, uint256 unstakeDelay, string url);
/**
* @dev Removes (deregisters) a relay. Unregistered (but staked for) relays can also be removed.
*
* Can only be called by the owner of the relay. After the relay's `unstakeDelay` has elapsed, {unstake} will be
* callable.
*
* Emits a {RelayRemoved} event.
*/
function removeRelayByOwner(address relay) external;
/**
* @dev Emitted when a relay is removed (deregistered). `unstakeTime` is the time when unstake will be callable.
*/
event RelayRemoved(address indexed relay, uint256 unstakeTime);
/** Deletes the relay from the system, and gives back its stake to the owner.
*
* Can only be called by the relay owner, after `unstakeDelay` has elapsed since {removeRelayByOwner} was called.
*
* Emits an {Unstaked} event.
*/
function unstake(address relay) external;
/**
* @dev Emitted when a relay is unstaked for, including the returned stake.
*/
event Unstaked(address indexed relay, uint256 stake);
// States a relay can be in
enum RelayState {
Unknown, // The relay is unknown to the system: it has never been staked for
Staked, // The relay has been staked for, but it is not yet active
Registered, // The relay has registered itself, and is active (can relay calls)
Removed // The relay has been removed by its owner and can no longer relay calls. It must wait for its unstakeDelay to elapse before it can unstake
}
/**
* @dev Returns a relay's status. Note that relays can be deleted when unstaked or penalized, causing this function
* to return an empty entry.
*/
function getRelay(address relay) external view returns (uint256 totalStake, uint256 unstakeDelay, uint256 unstakeTime, address payable owner, RelayState state);
// Balance management
/**
* @dev Deposits Ether for a contract, so that it can receive (and pay for) relayed transactions.
*
* Unused balance can only be withdrawn by the contract itself, by calling {withdraw}.
*
* Emits a {Deposited} event.
*/
function depositFor(address target) external payable;
/**
* @dev Emitted when {depositFor} is called, including the amount and account that was funded.
*/
event Deposited(address indexed recipient, address indexed from, uint256 amount);
/**
* @dev Returns an account's deposits. These can be either a contract's funds, or a relay owner's revenue.
*/
function balanceOf(address target) external view returns (uint256);
/**
* Withdraws from an account's balance, sending it back to it. Relay owners call this to retrieve their revenue, and
* contracts can use it to reduce their funding.
*
* Emits a {Withdrawn} event.
*/
function withdraw(uint256 amount, address payable dest) external;
/**
* @dev Emitted when an account withdraws funds from `RelayHub`.
*/
event Withdrawn(address indexed account, address indexed dest, uint256 amount);
// Relaying
/**
* @dev Checks if the `RelayHub` will accept a relayed operation.
* Multiple things must be true for this to happen:
* - all arguments must be signed for by the sender (`from`)
* - the sender's nonce must be the current one
* - the recipient must accept this transaction (via {acceptRelayedCall})
*
* Returns a `PreconditionCheck` value (`OK` when the transaction can be relayed), or a recipient-specific error
* code if it returns one in {acceptRelayedCall}.
*/
function canRelay(
address relay,
address from,
address to,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata signature,
bytes calldata approvalData
) external view returns (uint256 status, bytes memory recipientContext);
// Preconditions for relaying, checked by canRelay and returned as the corresponding numeric values.
enum PreconditionCheck {
OK, // All checks passed, the call can be relayed
WrongSignature, // The transaction to relay is not signed by requested sender
WrongNonce, // The provided nonce has already been used by the sender
AcceptRelayedCallReverted, // The recipient rejected this call via acceptRelayedCall
InvalidRecipientStatusCode // The recipient returned an invalid (reserved) status code
}
/**
* @dev Relays a transaction.
*
* For this to succeed, multiple conditions must be met:
* - {canRelay} must `return PreconditionCheck.OK`
* - the sender must be a registered relay
* - the transaction's gas price must be larger or equal to the one that was requested by the sender
* - the transaction must have enough gas to not run out of gas if all internal transactions (calls to the
* recipient) use all gas available to them
* - the recipient must have enough balance to pay the relay for the worst-case scenario (i.e. when all gas is
* spent)
*
* If all conditions are met, the call will be relayed and the recipient charged. {preRelayedCall}, the encoded
* function and {postRelayedCall} will be called in that order.
*
* Parameters:
* - `from`: the client originating the request
* - `to`: the target {IRelayRecipient} contract
* - `encodedFunction`: the function call to relay, including data
* - `transactionFee`: fee (%) the relay takes over actual gas cost
* - `gasPrice`: gas price the client is willing to pay
* - `gasLimit`: gas to forward when calling the encoded function
* - `nonce`: client's nonce
* - `signature`: client's signature over all previous params, plus the relay and RelayHub addresses
* - `approvalData`: dapp-specific data forwarded to {acceptRelayedCall}. This value is *not* verified by the
* `RelayHub`, but it still can be used for e.g. a signature.
*
* Emits a {TransactionRelayed} event.
*/
function relayCall(
address from,
address to,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata signature,
bytes calldata approvalData
) external;
/**
* @dev Emitted when an attempt to relay a call failed.
*
* This can happen due to incorrect {relayCall} arguments, or the recipient not accepting the relayed call. The
* actual relayed call was not executed, and the recipient not charged.
*
* The `reason` parameter contains an error code: values 1-10 correspond to `PreconditionCheck` entries, and values
* over 10 are custom recipient error codes returned from {acceptRelayedCall}.
*/
event CanRelayFailed(address indexed relay, address indexed from, address indexed to, bytes4 selector, uint256 reason);
/**
* @dev Emitted when a transaction is relayed.
* Useful when monitoring a relay's operation and relayed calls to a contract
*
* Note that the actual encoded function might be reverted: this is indicated in the `status` parameter.
*
* `charge` is the Ether value deducted from the recipient's balance, paid to the relay's owner.
*/
event TransactionRelayed(address indexed relay, address indexed from, address indexed to, bytes4 selector, RelayCallStatus status, uint256 charge);
// Reason error codes for the TransactionRelayed event
enum RelayCallStatus {
OK, // The transaction was successfully relayed and execution successful - never included in the event
RelayedCallFailed, // The transaction was relayed, but the relayed call failed
PreRelayedFailed, // The transaction was not relayed due to preRelatedCall reverting
PostRelayedFailed, // The transaction was relayed and reverted due to postRelatedCall reverting
RecipientBalanceChanged // The transaction was relayed and reverted due to the recipient's balance changing
}
/**
* @dev Returns how much gas should be forwarded to a call to {relayCall}, in order to relay a transaction that will
* spend up to `relayedCallStipend` gas.
*/
function requiredGas(uint256 relayedCallStipend) external view returns (uint256);
/**
* @dev Returns the maximum recipient charge, given the amount of gas forwarded, gas price and relay fee.
*/
function maxPossibleCharge(uint256 relayedCallStipend, uint256 gasPrice, uint256 transactionFee) external view returns (uint256);
// Relay penalization.
// Any account can penalize relays, removing them from the system immediately, and rewarding the
// reporter with half of the relay's stake. The other half is burned so that, even if the relay penalizes itself, it
// still loses half of its stake.
/**
* @dev Penalize a relay that signed two transactions using the same nonce (making only the first one valid) and
* different data (gas price, gas limit, etc. may be different).
*
* The (unsigned) transaction data and signature for both transactions must be provided.
*/
function penalizeRepeatedNonce(bytes calldata unsignedTx1, bytes calldata signature1, bytes calldata unsignedTx2, bytes calldata signature2) external;
/**
* @dev Penalize a relay that sent a transaction that didn't target ``RelayHub``'s {registerRelay} or {relayCall}.
*/
function penalizeIllegalTransaction(bytes calldata unsignedTx, bytes calldata signature) external;
/**
* @dev Emitted when a relay is penalized.
*/
event Penalized(address indexed relay, address sender, uint256 amount);
/**
* @dev Returns an account's nonce in `RelayHub`.
*/
function getNonce(address from) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Base interface for a contract that will be called via the GSN from {IRelayHub}.
*
* TIP: You don't need to write an implementation yourself! Inherit from {GSNRecipient} instead.
*/
interface IRelayRecipient {
/**
* @dev Returns the address of the {IRelayHub} instance this recipient interacts with.
*/
function getHubAddr() external view returns (address);
/**
* @dev Called by {IRelayHub} to validate if this recipient accepts being charged for a relayed call. Note that the
* recipient will be charged regardless of the execution result of the relayed call (i.e. if it reverts or not).
*
* The relay request was originated by `from` and will be served by `relay`. `encodedFunction` is the relayed call
* calldata, so its first four bytes are the function selector. The relayed call will be forwarded `gasLimit` gas,
* and the transaction executed with a gas price of at least `gasPrice`. ``relay``'s fee is `transactionFee`, and the
* recipient will be charged at most `maxPossibleCharge` (in wei). `nonce` is the sender's (`from`) nonce for
* replay attack protection in {IRelayHub}, and `approvalData` is a optional parameter that can be used to hold a signature
* over all or some of the previous values.
*
* Returns a tuple, where the first value is used to indicate approval (0) or rejection (custom non-zero error code,
* values 1 to 10 are reserved) and the second one is data to be passed to the other {IRelayRecipient} functions.
*
* {acceptRelayedCall} is called with 50k gas: if it runs out during execution, the request will be considered
* rejected. A regular revert will also trigger a rejection.
*/
function acceptRelayedCall(
address relay,
address from,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata approvalData,
uint256 maxPossibleCharge
)
external
view
returns (uint256, bytes memory);
/**
* @dev Called by {IRelayHub} on approved relay call requests, before the relayed call is executed. This allows to e.g.
* pre-charge the sender of the transaction.
*
* `context` is the second value returned in the tuple by {acceptRelayedCall}.
*
* Returns a value to be passed to {postRelayedCall}.
*
* {preRelayedCall} is called with 100k gas: if it runs out during execution or otherwise reverts, the relayed call
* will not be executed, but the recipient will still be charged for the transaction's cost.
*/
function preRelayedCall(bytes calldata context) external returns (bytes32);
/**
* @dev Called by {IRelayHub} on approved relay call requests, after the relayed call is executed. This allows to e.g.
* charge the user for the relayed call costs, return any overcharges from {preRelayedCall}, or perform
* contract-specific bookkeeping.
*
* `context` is the second value returned in the tuple by {acceptRelayedCall}. `success` is the execution status of
* the relayed call. `actualCharge` is an estimate of how much the recipient will be charged for the transaction,
* not including any gas used by {postRelayedCall} itself. `preRetVal` is {preRelayedCall}'s return value.
*
*
* {postRelayedCall} is called with 100k gas: if it runs out during execution or otherwise reverts, the relayed call
* and the call to {preRelayedCall} will be reverted retroactively, but the recipient will still be charged for the
* transaction's cost.
*/
function postRelayedCall(bytes calldata context, bool success, uint256 actualCharge, bytes32 preRetVal) external;
}
= Gas Station Network (GSN)
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/gsn
This set of contracts provide all the tools required to make a contract callable via the https://gsn.openzeppelin.com[Gas Station Network].
TIP: If you're new to the GSN, head over to our xref:learn::sending-gasless-transactions.adoc[overview of the system] and basic guide to xref:ROOT:gsn.adoc[creating a GSN-capable contract].
The core contract a recipient must inherit from is {GSNRecipient}: it includes all necessary interfaces, as well as some helper methods to make interacting with the GSN easier.
Utilities to make writing xref:ROOT:gsn-strategies.adoc[GSN strategies] easy are available in {GSNRecipient}, or you can simply use one of our pre-made strategies:
* {GSNRecipientERC20Fee} charges the end user for gas costs in an application-specific xref:ROOT:tokens.adoc#ERC20[ERC20 token]
* {GSNRecipientSignature} accepts all relayed calls that have been signed by a trusted third party (e.g. a private key in a backend)
You can also take a look at the two contract interfaces that make up the GSN protocol: {IRelayRecipient} and {IRelayHub}, but you won't need to use those directly.
== Recipient
{{GSNRecipient}}
== Strategies
{{GSNRecipientSignature}}
{{GSNRecipientERC20Fee}}
== Protocol
{{IRelayRecipient}}
{{IRelayHub}}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/EnumerableSet.sol";
import "../utils/Address.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
......@@ -23,7 +23,7 @@ abstract contract Ownable is Context {
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.9 <0.8.0;
pragma experimental ABIEncoderV2;
pragma solidity ^0.8.0;
import "./../math/SafeMath.sol";
import "./AccessControl.sol";
/**
......@@ -22,7 +20,6 @@ import "./AccessControl.sol";
* _Available since v3.3._
*/
contract TimelockController is AccessControl {
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
......@@ -54,7 +51,7 @@ contract TimelockController is AccessControl {
/**
* @dev Initializes the contract with a given `minDelay`.
*/
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) public {
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) {
_setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
......@@ -199,7 +196,7 @@ contract TimelockController is AccessControl {
require(!isOperation(id), "TimelockController: operation already scheduled");
require(delay >= getMinDelay(), "TimelockController: insufficient delay");
// solhint-disable-next-line not-rely-on-time
_timestamps[id] = SafeMath.add(block.timestamp, delay);
_timestamps[id] = block.timestamp + delay;
}
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle trees (hash trees),
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
......@@ -40,7 +40,7 @@ abstract contract EIP712 {
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) internal {
constructor(string memory name, string memory version) {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
_HASHED_NAME = hashedName;
......@@ -60,7 +60,7 @@ abstract contract EIP712 {
typeHash,
name,
version,
_getChainId(),
block.chainid,
address(this)
)
);
......@@ -85,14 +85,6 @@ abstract contract EIP712 {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
}
function _getChainId() private view returns (uint256 chainId) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.5 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20.sol";
import "./IERC20Permit.sol";
......@@ -31,7 +31,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
constructor(string memory name) internal EIP712(name, "1") {
constructor(string memory name) EIP712(name, "1") {
}
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
......@@ -13,18 +15,13 @@ library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Returns true if `account` supports the {IERC165} interface,
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
return _supportsERC165Interface(account, type(IERC165).interfaceId) &&
!_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
}
......@@ -103,29 +100,9 @@ library ERC165Checker {
* Interface identification is specified in ERC-165.
*/
function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
// success determines whether the staticcall succeeded and result determines
// whether the contract at account indicates support of _interfaceId
(bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
return (success && result);
}
/**
* @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return success true if the STATICCALL succeeded, false otherwise
* @return result true if the STATICCALL succeeded and the contract at account
* indicates support of the interface with identifier interfaceId, false otherwise
*/
function _callERC165SupportsInterface(address account, bytes4 interfaceId)
private
view
returns (bool, bool)
{
bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
bytes memory encodedParams = abi.encodeWithSelector(IERC165(account).supportsInterface.selector, interfaceId);
(bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
if (result.length < 32) return (false, false);
return (success, abi.decode(result, (bool)));
if (result.length < 32) return false;
return success && abi.decode(result, (bool));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ERC165.sol";
/**
* @dev Storage based implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
abstract contract ERC165Storage is ERC165 {
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC1820Implementer.sol";
......@@ -13,7 +13,7 @@ import "./IERC1820Implementer.sol";
* registration to be complete.
*/
contract ERC1820Implementer is IERC1820Implementer {
bytes32 constant private _ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");
mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface for an ERC1820 implementer, as defined in the
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the global ERC1820 Registry, as defined in the
......
......@@ -20,6 +20,8 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
{{ERC165}}
{{ERC165Storage}}
{{ERC165Checker}}
== Global
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
* @dev Wrappers over Solidity's arithmetic operations.
*/
library SafeMath {
/**
......@@ -22,9 +12,11 @@ library SafeMath {
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
......@@ -33,8 +25,10 @@ library SafeMath {
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
......@@ -43,13 +37,15 @@ library SafeMath {
* _Available since v3.4._
*/
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);
unchecked {
// 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);
}
}
/**
......@@ -58,8 +54,10 @@ library SafeMath {
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
......@@ -68,8 +66,10 @@ library SafeMath {
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
......@@ -83,9 +83,7 @@ library SafeMath {
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
return a + b;
}
/**
......@@ -99,7 +97,6 @@ library SafeMath {
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
......@@ -114,26 +111,20 @@ library SafeMath {
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* 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).
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
......@@ -150,7 +141,6 @@ library SafeMath {
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
......@@ -168,16 +158,19 @@ library SafeMath {
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* 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
......@@ -188,8 +181,10 @@ library SafeMath {
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
......@@ -208,7 +203,9 @@ library SafeMath {
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
* @dev Signed math operations that revert on error.
*/
library SignedSafeMath {
int256 constant private _INT256_MIN = -2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
......@@ -20,40 +18,21 @@ library SignedSafeMath {
* - Multiplication cannot overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// 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 0;
}
require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
return a * b;
}
/**
* @dev Returns the integer division of two signed integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* 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).
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
return a / b;
}
/**
......@@ -67,10 +46,7 @@ library SignedSafeMath {
* - Subtraction cannot overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
return a - b;
}
/**
......@@ -84,9 +60,6 @@ library SignedSafeMath {
* - Addition cannot overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
return a + b;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
contract AccessControlMock is AccessControl {
constructor() public {
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Address.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Arrays.sol";
......@@ -9,7 +9,7 @@ contract ArraysImpl {
uint256[] private _array;
constructor (uint256[] memory array) public {
constructor (uint256[] memory array) {
_array = array;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
contract BadBeaconNoImpl {
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
contract CallReceiverMock {
string public sharedAnswer;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../proxy/Clones.sol";
import "../utils/Address.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../payment/escrow/ConditionalEscrow.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Counters.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Create2.sol";
import "../introspection/ERC1820Implementer.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
abstract contract Impl {
function version() public pure virtual returns (string memory);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../cryptography/ECDSA.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../drafts/EIP712.sol";
import "../cryptography/ECDSA.sol";
contract EIP712External is EIP712 {
constructor(string memory name, string memory version) public EIP712(name, version) {}
constructor(string memory name, string memory version) EIP712(name, version) {}
function domainSeparator() external view returns (bytes32) {
return _domainSeparatorV4();
......@@ -22,11 +22,7 @@ contract EIP712External is EIP712 {
require(recoveredSigner == signer);
}
function getChainId() external view returns (uint256 chainId) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
function getChainId() external view returns (uint256) {
return block.chainid;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC1155/ERC1155Burnable.sol";
contract ERC1155BurnableMock is ERC1155Burnable {
constructor(string memory uri) public ERC1155(uri) { }
constructor(string memory uri) ERC1155(uri) { }
function mint(address to, uint256 id, uint256 value, bytes memory data) public {
_mint(to, id, value, data);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC1155/ERC1155.sol";
......@@ -9,7 +9,7 @@ import "../token/ERC1155/ERC1155.sol";
* This mock just publicizes internal functions for testing purposes
*/
contract ERC1155Mock is ERC1155 {
constructor (string memory uri) public ERC1155(uri) {
constructor (string memory uri) ERC1155(uri) {
// solhint-disable-previous-line no-empty-blocks
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC1155Mock.sol";
import "../token/ERC1155/ERC1155Pausable.sol";
contract ERC1155PausableMock is ERC1155Mock, ERC1155Pausable {
constructor(string memory uri) public ERC1155Mock(uri) { }
constructor(string memory uri) ERC1155Mock(uri) { }
function pause() external {
_pause();
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../introspection/ERC165.sol";
import "../token/ERC1155/IERC1155Receiver.sol";
import "./ERC165Mock.sol";
contract ERC1155ReceiverMock is IERC1155Receiver, ERC165Mock {
contract ERC1155ReceiverMock is IERC1155Receiver, ERC165 {
bytes4 private _recRetval;
bool private _recReverts;
bytes4 private _batRetval;
......@@ -20,7 +20,6 @@ contract ERC1155ReceiverMock is IERC1155Receiver, ERC165Mock {
bytes4 batRetval,
bool batReverts
)
public
{
_recRetval = recRetval;
_recReverts = recReverts;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../introspection/IERC165.sol";
......@@ -29,7 +29,7 @@ contract SupportsInterfaceWithLookupMock is IERC165 {
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself.
*/
constructor () public {
constructor () {
_registerInterface(INTERFACE_ID_ERC165);
}
......@@ -50,7 +50,7 @@ contract SupportsInterfaceWithLookupMock is IERC165 {
}
contract ERC165InterfacesSupported is SupportsInterfaceWithLookupMock {
constructor (bytes4[] memory interfaceIds) public {
constructor (bytes4[] memory interfaceIds) {
for (uint256 i = 0; i < interfaceIds.length; i++) {
_registerInterface(interfaceIds[i]);
}
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ERC165MissingData {
function supportsInterface(bytes4 interfaceId) public view {} // missing return
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
contract ERC165NotSupported { }
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../introspection/ERC165Checker.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../introspection/ERC165.sol";
contract ERC165Mock is ERC165 {
function registerInterface(bytes4 interfaceId) public {
_registerInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../introspection/ERC165Storage.sol";
contract ERC165StorageMock is ERC165Storage {
function registerInterface(bytes4 interfaceId) public {
_registerInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../introspection/ERC1820Implementer.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Burnable.sol";
......@@ -10,7 +10,7 @@ contract ERC20BurnableMock is ERC20Burnable {
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
) ERC20(name, symbol) {
_mint(initialAccount, initialBalance);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Capped.sol";
contract ERC20CappedMock is ERC20Capped {
constructor (string memory name, string memory symbol, uint256 cap)
public ERC20(name, symbol) ERC20Capped(cap)
ERC20(name, symbol) ERC20Capped(cap)
{ }
function mint(address to, uint256 tokenId) public {
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20.sol";
contract ERC20DecimalsMock is ERC20 {
constructor (string memory name, string memory symbol, uint8 decimals) public ERC20(name, symbol) {
_setupDecimals(decimals);
uint8 immutable private _decimals;
constructor (string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) {
_decimals = decimals_;
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20.sol";
......@@ -11,7 +11,7 @@ contract ERC20Mock is ERC20 {
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable ERC20(name, symbol) {
) payable ERC20(name, symbol) {
_mint(initialAccount, initialBalance);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Pausable.sol";
......@@ -11,7 +11,7 @@ contract ERC20PausableMock is ERC20Pausable {
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
) ERC20(name, symbol) {
_mint(initialAccount, initialBalance);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../drafts/ERC20Permit.sol";
......@@ -10,15 +10,11 @@ contract ERC20PermitMock is ERC20Permit {
string memory symbol,
address initialAccount,
uint256 initialBalance
) public payable ERC20(name, symbol) ERC20Permit(name) {
) payable ERC20(name, symbol) ERC20Permit(name) {
_mint(initialAccount, initialBalance);
}
function getChainId() external view returns (uint256 chainId) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
function getChainId() external view returns (uint256) {
return block.chainid;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Snapshot.sol";
......@@ -11,7 +11,7 @@ contract ERC20SnapshotMock is ERC20Snapshot {
string memory symbol,
address initialAccount,
uint256 initialBalance
) public ERC20(name, symbol) {
) ERC20(name, symbol) {
_mint(initialAccount, initialBalance);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721Burnable.sol";
contract ERC721BurnableMock is ERC721Burnable {
constructor(string memory name, string memory symbol) public ERC721(name, symbol) { }
constructor(string memory name, string memory symbol) ERC721(name, symbol) { }
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../token/ERC721/ERC721.sol";
import "../GSN/GSNRecipient.sol";
import "../GSN/GSNRecipientSignature.sol";
/**
* @title ERC721GSNRecipientMock
* A simple ERC721 mock that has GSN support enabled
*/
contract ERC721GSNRecipientMock is ERC721, GSNRecipient, GSNRecipientSignature {
constructor(string memory name, string memory symbol, address trustedSigner)
public
ERC721(name, symbol)
GSNRecipientSignature(trustedSigner)
{ }
function mint(uint256 tokenId) public {
_mint(_msgSender(), tokenId);
}
function _msgSender() internal view override(Context, GSNRecipient) returns (address payable) {
return GSNRecipient._msgSender();
}
function _msgData() internal view override(Context, GSNRecipient) returns (bytes memory) {
return GSNRecipient._msgData();
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721.sol";
......@@ -9,7 +9,7 @@ import "../token/ERC721/ERC721.sol";
* This mock just provides a public safeMint, mint, and burn functions for testing purposes
*/
contract ERC721Mock is ERC721 {
constructor (string memory name, string memory symbol) public ERC721(name, symbol) { }
constructor (string memory name, string memory symbol) ERC721(name, symbol) { }
function exists(uint256 tokenId) public view returns (bool) {
return _exists(tokenId);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC721/ERC721Pausable.sol";
......@@ -9,7 +9,7 @@ import "../token/ERC721/ERC721Pausable.sol";
* This mock just provides a public mint, burn and exists functions for testing purposes
*/
contract ERC721PausableMock is ERC721Pausable {
constructor (string memory name, string memory symbol) public ERC721(name, symbol) { }
constructor (string memory name, string memory symbol) ERC721(name, symbol) { }
function mint(address to, uint256 tokenId) public {
super._mint(to, tokenId);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721Receiver.sol";
contract ERC721ReceiverMock is IERC721Receiver {
bytes4 private _retval;
bool private _reverts;
enum Error {
None,
RevertWithMessage,
RevertWithoutMessage,
Panic
}
bytes4 private immutable _retval;
Error private immutable _error;
event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas);
constructor (bytes4 retval, bool reverts) public {
constructor (bytes4 retval, Error error) {
_retval = retval;
_reverts = reverts;
_error = error;
}
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public override returns (bytes4)
{
require(!_reverts, "ERC721ReceiverMock: reverting");
if (_error == Error.RevertWithMessage) {
revert("ERC721ReceiverMock: reverting");
} else if (_error == Error.RevertWithoutMessage) {
revert();
} else if (_error == Error.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}
emit Received(operator, from, tokenId, data, gasleft());
return _retval;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
import "../token/ERC777/ERC777.sol";
......@@ -14,7 +14,7 @@ contract ERC777Mock is Context, ERC777 {
string memory name,
string memory symbol,
address[] memory defaultOperators
) public ERC777(name, symbol, defaultOperators) {
) ERC777(name, symbol, defaultOperators) {
_mint(initialHolder, initialBalance, "", "");
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
import "../token/ERC777/IERC777.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/EnumerableMap.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/EnumerableSet.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
contract EtherReceiverMock {
bool private _acceptEther;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../GSN/GSNRecipient.sol";
import "../GSN/GSNRecipientERC20Fee.sol";
contract GSNRecipientERC20FeeMock is GSNRecipient, GSNRecipientERC20Fee {
constructor(string memory name, string memory symbol) public GSNRecipientERC20Fee(name, symbol) { }
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
event MockFunctionCalled(uint256 senderBalance);
function mockFunction() public {
emit MockFunctionCalled(token().balanceOf(_msgSender()));
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./ContextMock.sol";
import "../GSN/GSNRecipient.sol";
// By inheriting from GSNRecipient, Context's internal functions are overridden automatically
contract GSNRecipientMock is ContextMock, GSNRecipient {
function withdrawDeposits(uint256 amount, address payable payee) public {
_withdrawDeposits(amount, payee);
}
function acceptRelayedCall(address, address, bytes calldata, uint256, uint256, uint256, uint256, bytes calldata, uint256)
external
view
override
returns (uint256, bytes memory)
{
return (0, "");
}
function _preRelayedCall(bytes memory) internal override returns (bytes32) { }
function _postRelayedCall(bytes memory, bool, uint256, bytes32) internal override { }
function upgradeRelayHub(address newRelayHub) public {
return _upgradeRelayHub(newRelayHub);
}
function _msgSender() internal override(Context, GSNRecipient) view virtual returns (address payable) {
return GSNRecipient._msgSender();
}
function _msgData() internal override(Context, GSNRecipient) view virtual returns (bytes memory) {
return GSNRecipient._msgData();
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../GSN/GSNRecipient.sol";
import "../GSN/GSNRecipientSignature.sol";
contract GSNRecipientSignatureMock is GSNRecipient, GSNRecipientSignature {
constructor(address trustedSigner) public GSNRecipientSignature(trustedSigner) { }
event MockFunctionCalled();
function mockFunction() public {
emit MockFunctionCalled();
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../math/Math.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import { MerkleProof } from "../cryptography/MerkleProof.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/Ownable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Pausable.sol";
......@@ -8,7 +8,7 @@ contract PausableMock is Pausable {
bool public drasticMeasureTaken;
uint256 public count;
constructor () public {
constructor () {
drasticMeasureTaken = false;
count = 0;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../payment/PullPayment.sol";
// mock class using PullPayment
contract PullPaymentMock is PullPayment {
constructor () public payable { }
constructor () payable { }
// test helper function to call asyncTransfer
function callTransfer(address dest, uint256 amount) public {
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
contract ReentrancyAttack is Context {
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/ReentrancyGuard.sol";
import "./ReentrancyAttack.sol";
......@@ -8,7 +8,7 @@ import "./ReentrancyAttack.sol";
contract ReentrancyMock is ReentrancyGuard {
uint256 public counter;
constructor () public {
constructor () {
counter = 0;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/SafeCast.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
import "../token/ERC20/IERC20.sol";
......@@ -98,7 +98,7 @@ contract SafeERC20Wrapper is Context {
IERC20 private _token;
constructor (IERC20 token) public {
constructor (IERC20 token) {
_token = token;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../math/SafeMath.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../math/SignedSafeMath.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../proxy/Initializable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Strings.sol";
......@@ -8,4 +8,10 @@ contract StringsMock {
function fromUint256(uint256 value) public pure returns (string memory) {
return Strings.toString(value);
}
function fromUint256Hex(uint256 value) public pure returns (string memory) {
return Strings.toHexString(value);
}
function fromUint256HexFixed(uint256 value, uint256 length) public pure returns (string memory) {
return Strings.toHexString(value, length);
}
}
{
"name": "@openzeppelin/contracts-upgradeable",
"description": "Secure Smart Contract library for Solidity",
"version": "3.3.0",
"version": "3.4.0",
"files": [
"**/*.sol",
"/build/contracts/*.json",
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Context.sol";
import "../math/SafeMath.sol";
......@@ -20,8 +20,6 @@ import "../utils/Address.sol";
* function.
*/
contract PaymentSplitter is Context {
using SafeMath for uint256;
event PayeeAdded(address account, uint256 shares);
event PaymentReleased(address to, uint256 amount);
event PaymentReceived(address from, uint256 amount);
......@@ -40,7 +38,7 @@ contract PaymentSplitter is Context {
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
* duplicates in `payees`.
*/
constructor (address[] memory payees, uint256[] memory shares_) public payable {
constructor (address[] memory payees, uint256[] memory shares_) payable {
// solhint-disable-next-line max-line-length
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
require(payees.length > 0, "PaymentSplitter: no payees");
......@@ -105,13 +103,13 @@ contract PaymentSplitter is Context {
function release(address payable account) public virtual {
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 totalReceived = address(this).balance.add(_totalReleased);
uint256 payment = totalReceived.mul(_shares[account]).div(_totalShares).sub(_released[account]);
uint256 totalReceived = address(this).balance + _totalReleased;
uint256 payment = totalReceived * _shares[account] / _totalShares - _released[account];
require(payment != 0, "PaymentSplitter: account is not due payment");
_released[account] = _released[account].add(payment);
_totalReleased = _totalReleased.add(payment);
_released[account] = _released[account] + payment;
_totalReleased = _totalReleased + payment;
Address.sendValue(account, payment);
emit PaymentReleased(account, payment);
......@@ -129,7 +127,7 @@ contract PaymentSplitter is Context {
_payees.push(account);
_shares[account] = shares_;
_totalShares = _totalShares.add(shares_);
_totalShares = _totalShares + shares_;
emit PayeeAdded(account, shares_);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "./escrow/Escrow.sol";
......@@ -23,9 +23,9 @@ import "./escrow/Escrow.sol";
* payments with {payments}, and retrieve them with {withdrawPayments}.
*/
abstract contract PullPayment {
Escrow private _escrow;
Escrow immutable private _escrow;
constructor () internal {
constructor () {
_escrow = new Escrow();
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./Escrow.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../math/SafeMath.sol";
import "../../access/Ownable.sol";
import "../../utils/Address.sol";
......@@ -20,7 +19,6 @@ import "../../utils/Address.sol";
* to the escrow's deposit and withdraw.
*/
contract Escrow is Ownable {
using SafeMath for uint256;
using Address for address payable;
event Deposited(address indexed payee, uint256 weiAmount);
......@@ -38,7 +36,7 @@ contract Escrow is Ownable {
*/
function deposit(address payee) public payable virtual onlyOwner {
uint256 amount = msg.value;
_deposits[payee] = _deposits[payee].add(amount);
_deposits[payee] = _deposits[payee] + amount;
emit Deposited(payee, amount);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ConditionalEscrow.sol";
......@@ -15,19 +15,21 @@ import "./ConditionalEscrow.sol";
* with `RefundEscrow` will be made through the owner contract.
*/
contract RefundEscrow is ConditionalEscrow {
using Address for address payable;
enum State { Active, Refunding, Closed }
event RefundsClosed();
event RefundsEnabled();
State private _state;
address payable private _beneficiary;
address payable immutable private _beneficiary;
/**
* @dev Constructor.
* @param beneficiary_ The beneficiary of the deposits.
*/
constructor (address payable beneficiary_) public {
constructor (address payable beneficiary_) {
require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address");
_beneficiary = beneficiary_;
_state = State.Active;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
import "../utils/Context.sol";
......@@ -30,7 +30,7 @@ contract ERC1155PresetMinterPauser is Context, AccessControl, ERC1155Burnable, E
* @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that
* deploys the contract.
*/
constructor(string memory uri) public ERC1155(uri) {
constructor(string memory uri) ERC1155(uri) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
pragma solidity ^0.8.0;
import "../token/ERC20/ERC20Burnable.sol";
......@@ -17,7 +17,7 @@ import "../token/ERC20/ERC20Burnable.sol";
*/
contract ERC20PresetFixedSupply is ERC20Burnable {
/**
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
*
* See {ERC20-constructor}.
*/
......@@ -26,7 +26,7 @@ contract ERC20PresetFixedSupply is ERC20Burnable {
string memory symbol,
uint256 initialSupply,
address owner
) public ERC20(name, symbol) {
) ERC20(name, symbol) {
_mint(owner, initialSupply);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
import "../utils/Context.sol";
......@@ -32,7 +32,7 @@ contract ERC20PresetMinterPauser is Context, AccessControl, ERC20Burnable, ERC20
*
* See {ERC20-constructor}.
*/
constructor(string memory name, string memory symbol) public ERC20(name, symbol) {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
import "../utils/Context.sol";
......@@ -39,7 +39,7 @@ contract ERC721PresetMinterPauserAutoId is Context, AccessControl, ERC721Burnabl
* Token URIs will be autogenerated based on `baseURI` and their token IDs.
* See {ERC721-tokenURI}.
*/
constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
constructor(string memory name, string memory symbol, string memory baseURI) ERC721(name, symbol) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
pragma solidity ^0.8.0;
import "../token/ERC777/ERC777.sol";
......@@ -13,7 +13,7 @@ import "../token/ERC777/ERC777.sol";
*/
contract ERC777PresetFixedSupply is ERC777 {
/**
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
* @dev Mints `initialSupply` amount of token and transfers them to `owner`.
*
* See {ERC777-constructor}.
*/
......@@ -23,7 +23,7 @@ contract ERC777PresetFixedSupply is ERC777 {
address[] memory defaultOperators,
uint256 initialSupply,
address owner
) public ERC777(name, symbol, defaultOperators) {
) ERC777(name, symbol, defaultOperators) {
_mint(owner, initialSupply, "", "");
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./Proxy.sol";
import "../utils/Address.sol";
......@@ -32,7 +32,7 @@ contract BeaconProxy is Proxy {
*
* - `beacon` must be a contract with the interface {IBeacon}.
*/
constructor(address beacon, bytes memory data) public payable {
constructor(address beacon, bytes memory data) payable {
assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
_setBeacon(beacon, data);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
......
// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;
pragma solidity ^0.8.0;
import "../utils/Address.sol";
......@@ -33,7 +33,7 @@ abstract contract Initializable {
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
......@@ -47,9 +47,4 @@ abstract contract Initializable {
_initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
return !Address.isContract(address(this));
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../access/Ownable.sol";
import "./TransparentUpgradeableProxy.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./UpgradeableProxy.sol";
......@@ -30,7 +30,7 @@ contract TransparentUpgradeableProxy is UpgradeableProxy {
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
* optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
*/
constructor(address _logic, address admin_, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
constructor(address _logic, address admin_, bytes memory _data) payable UpgradeableProxy(_logic, _data) {
assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
_setAdmin(admin_);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IBeacon.sol";
import "../access/Ownable.sol";
......@@ -24,7 +24,7 @@ contract UpgradeableBeacon is IBeacon, Ownable {
* @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
* beacon.
*/
constructor(address implementation_) public {
constructor(address implementation_) {
_setImplementation(implementation_);
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./Proxy.sol";
import "../utils/Address.sol";
......@@ -21,7 +21,7 @@ contract UpgradeableProxy is Proxy {
* 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.
*/
constructor(address _logic, bytes memory _data) public payable {
constructor(address _logic, bytes memory _data) payable {
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_setImplementation(_logic);
if(_data.length > 0) {
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC1155.sol";
import "./IERC1155MetadataURI.sol";
import "./IERC1155Receiver.sol";
import "../../utils/Context.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
......@@ -19,7 +18,6 @@ import "../../utils/Address.sol";
* _Available since v3.1._
*/
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
using SafeMath for uint256;
using Address for address;
// Mapping from token ID to account balances
......@@ -31,35 +29,20 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
/*
* bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
* bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
* bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
*
* => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
* 0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
*/
bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
/*
* bytes4(keccak256('uri(uint256)')) == 0x0e89341c
*/
bytes4 private constant _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;
/**
* @dev See {_setURI}.
*/
constructor (string memory uri_) public {
constructor (string memory uri_) {
_setURI(uri_);
}
// register the supported interfaces to conform to ERC1155 via ERC165
_registerInterface(_INTERFACE_ID_ERC1155);
// register the supported interfaces to conform to ERC1155MetadataURI via ERC165
_registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155).interfaceId
|| interfaceId == type(IERC1155MetadataURI).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
......@@ -157,8 +140,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
_beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
_balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
_balances[id][to] = _balances[id][to].add(amount);
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
_balances[id][from] = fromBalance - amount;
_balances[id][to] += amount;
emit TransferSingle(operator, from, to, id, amount);
......@@ -194,11 +179,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256 id = ids[i];
uint256 amount = amounts[i];
_balances[id][from] = _balances[id][from].sub(
amount,
"ERC1155: insufficient balance for transfer"
);
_balances[id][to] = _balances[id][to].add(amount);
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
_balances[id][from] = fromBalance - amount;
_balances[id][to] += amount;
}
emit TransferBatch(operator, from, to, ids, amounts);
......@@ -247,7 +231,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
_beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
_balances[id][account] = _balances[id][account].add(amount);
_balances[id][account] += amount;
emit TransferSingle(operator, address(0), account, id, amount);
_doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
......@@ -271,7 +255,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
for (uint i = 0; i < ids.length; i++) {
_balances[ids[i]][to] = amounts[i].add(_balances[ids[i]][to]);
_balances[ids[i]][to] += amounts[i];
}
emit TransferBatch(operator, address(0), to, ids, amounts);
......@@ -294,10 +278,9 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
_beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
_balances[id][account] = _balances[id][account].sub(
amount,
"ERC1155: burn amount exceeds balance"
);
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
_balances[id][account] = accountBalance - amount;
emit TransferSingle(operator, account, address(0), id, amount);
}
......@@ -318,10 +301,12 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
_beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
for (uint i = 0; i < ids.length; i++) {
_balances[ids[i]][account] = _balances[ids[i]][account].sub(
amounts[i],
"ERC1155: burn amount exceeds balance"
);
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
_balances[id][account] = accountBalance - amount;
}
emit TransferBatch(operator, account, address(0), ids, amounts);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC1155.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC1155Receiver.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC1155.sol";
import "../../utils/Pausable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC1155Receiver.sol";
import "../../introspection/ERC165.sol";
......@@ -9,10 +9,11 @@ import "../../introspection/ERC165.sol";
* @dev _Available since v3.1._
*/
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
constructor() internal {
_registerInterface(
ERC1155Receiver(address(0)).onERC1155Received.selector ^
ERC1155Receiver(address(0)).onERC1155BatchReceived.selector
);
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId
|| super.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "../../introspection/IERC165.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC1155.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../introspection/IERC165.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
/**
* @dev Implementation of the {IERC20} interface.
......@@ -31,8 +30,6 @@ import "../../math/SafeMath.sol";
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
......@@ -41,21 +38,19 @@ contract ERC20 is Context, IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
* @dev Sets the values for {name} and {symbol}.
*
* To select a different value for {decimals}, use {_setupDecimals}.
* The defaut value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name_, string memory symbol_) public {
constructor (string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
......@@ -79,15 +74,15 @@ contract ERC20 is Context, IERC20 {
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overloaded;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return _decimals;
return 18;
}
/**
......@@ -151,7 +146,11 @@ contract ERC20 is Context, IERC20 {
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
_approve(sender, _msgSender(), currentAllowance - amount);
return true;
}
......@@ -168,7 +167,7 @@ contract ERC20 is Context, IERC20 {
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
return true;
}
......@@ -187,7 +186,10 @@ contract ERC20 is Context, IERC20 {
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
return true;
}
......@@ -211,8 +213,11 @@ contract ERC20 is Context, IERC20 {
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
_balances[sender] = senderBalance - amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
......@@ -230,8 +235,8 @@ contract ERC20 is Context, IERC20 {
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
......@@ -251,8 +256,11 @@ contract ERC20 is Context, IERC20 {
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
......@@ -278,17 +286,6 @@ contract ERC20 is Context, IERC20 {
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "./ERC20.sol";
......@@ -11,8 +11,6 @@ import "./ERC20.sol";
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
using SafeMath for uint256;
/**
* @dev Destroys `amount` tokens from the caller.
*
......@@ -34,9 +32,9 @@ abstract contract ERC20Burnable is Context, ERC20 {
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
uint256 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), currentAllowance - amount);
_burn(account, amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC20.sol";
......@@ -8,15 +8,13 @@ import "./ERC20.sol";
* @dev Extension of {ERC20} that adds a cap to the supply of tokens.
*/
abstract contract ERC20Capped is ERC20 {
using SafeMath for uint256;
uint256 private _cap;
uint256 immutable private _cap;
/**
* @dev Sets the value of the `cap`. This value is immutable, it can only be
* set once during construction.
*/
constructor (uint256 cap_) internal {
constructor (uint256 cap_) {
require(cap_ > 0, "ERC20Capped: cap is 0");
_cap = cap_;
}
......@@ -39,7 +37,7 @@ abstract contract ERC20Capped is ERC20 {
super._beforeTokenTransfer(from, to, amount);
if (from == address(0)) { // When minting tokens
require(totalSupply().add(amount) <= cap(), "ERC20Capped: cap exceeded");
require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC20.sol";
import "../../utils/Pausable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../math/SafeMath.sol";
import "../../utils/Arrays.sol";
import "../../utils/Counters.sol";
import "./ERC20.sol";
......@@ -35,7 +34,6 @@ abstract contract ERC20Snapshot is ERC20 {
// Inspired by Jordi Baylina's MiniMeToken to record historical balances:
// https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol
using SafeMath for uint256;
using Arrays for uint256[];
using Counters for Counters.Counter;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
......@@ -16,7 +15,6 @@ import "../../utils/Address.sol";
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
......@@ -46,13 +44,17 @@ library SafeERC20 {
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./SafeERC20.sol";
......@@ -15,15 +15,15 @@ contract TokenTimelock {
using SafeERC20 for IERC20;
// ERC20 basic token contract being held
IERC20 private _token;
IERC20 immutable private _token;
// beneficiary of tokens after they are released
address private _beneficiary;
address immutable private _beneficiary;
// timestamp when token release is enabled
uint256 private _releaseTime;
uint256 immutable private _releaseTime;
constructor (IERC20 token_, address beneficiary_, uint256 releaseTime_) public {
constructor (IERC20 token_, address beneficiary_, uint256 releaseTime_) {
// solhint-disable-next-line not-rely-on-time
require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time");
_token = token_;
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "./IERC721.sol";
......@@ -8,7 +8,6 @@ import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol";
import "./IERC721Receiver.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../utils/EnumerableSet.sol";
import "../../utils/EnumerableMap.sol";
......@@ -19,16 +18,11 @@ import "../../utils/Strings.sol";
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableMap for EnumerableMap.UintToAddressMap;
using Strings for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSet.UintSet) private _holderTokens;
......@@ -53,51 +47,22 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor (string memory name_, string memory symbol_) public {
constructor (string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC721).interfaceId
|| interfaceId == type(IERC721Metadata).interfaceId
|| interfaceId == type(IERC721Enumerable).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
......@@ -435,18 +400,22 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
// solhint-disable-next-line no-inline-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
function _approve(address to, uint256 tokenId) private {
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "./ERC721.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC721Receiver.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./ERC721.sol";
import "../../utils/Pausable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "../../introspection/IERC165.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC721.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
import "./IERC721.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "./IERC777.sol";
import "./IERC777Recipient.sol";
import "./IERC777Sender.sol";
import "../../token/ERC20/IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../introspection/IERC1820Registry.sol";
......@@ -27,7 +26,6 @@ import "../../introspection/IERC1820Registry.sol";
* destroyed. This makes integration with ERC20 applications seamless.
*/
contract ERC777 is Context, IERC777, IERC20 {
using SafeMath for uint256;
using Address for address;
IERC1820Registry constant internal _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
......@@ -39,16 +37,8 @@ contract ERC777 is Context, IERC777, IERC20 {
string private _name;
string private _symbol;
// We inline the result of the following hashes because Solidity doesn't resolve them at compile time.
// See https://github.com/ethereum/solidity/issues/4024.
// keccak256("ERC777TokensSender")
bytes32 constant private _TOKENS_SENDER_INTERFACE_HASH =
0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
// keccak256("ERC777TokensRecipient")
bytes32 constant private _TOKENS_RECIPIENT_INTERFACE_HASH =
0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
// This isn't ever read from - it's only used to respond to the defaultOperators query.
address[] private _defaultOperatorsArray;
......@@ -70,9 +60,7 @@ contract ERC777 is Context, IERC777, IERC20 {
string memory name_,
string memory symbol_,
address[] memory defaultOperators_
)
public
{
) {
_name = name_;
_symbol = symbol_;
......@@ -289,7 +277,10 @@ contract ERC777 is Context, IERC777, IERC20 {
_callTokensToSend(spender, holder, recipient, amount, "", "");
_move(spender, holder, recipient, amount, "", "");
_approve(holder, spender, _allowances[holder][spender].sub(amount, "ERC777: transfer amount exceeds allowance"));
uint256 currentAllowance = _allowances[holder][spender];
require(currentAllowance >= amount, "ERC777: transfer amount exceeds allowance");
_approve(holder, spender, currentAllowance - amount);
_callTokensReceived(spender, holder, recipient, amount, "", "", false);
......@@ -329,8 +320,8 @@ contract ERC777 is Context, IERC777, IERC20 {
_beforeTokenTransfer(operator, address(0), account, amount);
// Update state variables
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
_totalSupply += amount;
_balances[account] += amount;
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);
......@@ -395,8 +386,10 @@ contract ERC777 is Context, IERC777, IERC20 {
_beforeTokenTransfer(operator, from, address(0), amount);
// Update state variables
_balances[from] = _balances[from].sub(amount, "ERC777: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: burn amount exceeds balance");
_balances[from] = fromBalance - amount;
_totalSupply -= amount;
emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
......@@ -414,8 +407,10 @@ contract ERC777 is Context, IERC777, IERC20 {
{
_beforeTokenTransfer(operator, from, to, amount);
_balances[from] = _balances[from].sub(amount, "ERC777: transfer amount exceeds balance");
_balances[to] = _balances[to].add(amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: transfer amount exceeds balance");
_balances[from] = fromBalance - amount;
_balances[to] += amount;
emit Sent(operator, from, to, amount, userData, operatorData);
emit Transfer(from, to, amount);
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC777Token standard as defined in the EIP.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC777TokensSender standard as defined in the EIP.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "../math/Math.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^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
* manner, since when dealing with 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) {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
function _msgData() internal view virtual returns (bytes calldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../math/SafeMath.sol";
pragma solidity ^0.8.0;
/**
* @title Counters
......@@ -11,13 +9,8 @@ import "../math/SafeMath.sol";
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
* Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
* overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
* directly accessed.
*/
library Counters {
using SafeMath for uint256;
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
......@@ -30,11 +23,16 @@ library Counters {
}
function increment(Counter storage counter) internal {
// The {SafeMath} overflow check can be skipped here, see the comment at the top
counter._value += 1;
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
counter._value = counter._value.sub(1);
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
......@@ -27,6 +29,8 @@ pragma solidity >=0.6.0 <0.8.0;
* supported.
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
......@@ -36,18 +40,11 @@ library EnumerableMap {
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct MapEntry {
bytes32 _key;
bytes32 _value;
}
struct Map {
// Storage of map keys and values
MapEntry[] _entries;
// Storage of keys
EnumerableSet.Bytes32Set _keys;
// Position of the entry defined by a key in the `entries` array, plus 1
// because index 0 means a key is not in the map.
mapping (bytes32 => uint256) _indexes;
mapping (bytes32 => bytes32) _values;
}
/**
......@@ -58,19 +55,8 @@ library EnumerableMap {
* already present.
*/
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) { // Equivalent to !contains(map, key)
map._entries.push(MapEntry({ _key: key, _value: value }));
// The entry is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
map._indexes[key] = map._entries.length;
return true;
} else {
map._entries[keyIndex - 1]._value = value;
return false;
}
map._values[key] = value;
return map._keys.add(key);
}
/**
......@@ -79,51 +65,22 @@ library EnumerableMap {
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage map, bytes32 key) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex != 0) { // Equivalent to contains(map, key)
// To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
// in the array, and then remove the last entry (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = keyIndex - 1;
uint256 lastIndex = map._entries.length - 1;
// When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
MapEntry storage lastEntry = map._entries[lastIndex];
// Move the last entry to the index where the entry to delete is
map._entries[toDeleteIndex] = lastEntry;
// Update the index for the moved entry
map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved entry was stored
map._entries.pop();
// Delete the index for the deleted slot
delete map._indexes[key];
return true;
} else {
return false;
}
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._indexes[key] != 0;
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._entries.length;
return map._keys.length();
}
/**
......@@ -137,10 +94,8 @@ library EnumerableMap {
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
require(map._entries.length > index, "EnumerableMap: index out of bounds");
MapEntry storage entry = map._entries[index];
return (entry._key, entry._value);
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
......@@ -148,9 +103,12 @@ library EnumerableMap {
* 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
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (_contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
......@@ -161,9 +119,9 @@ library EnumerableMap {
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
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
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
......@@ -173,9 +131,9 @@ library EnumerableMap {
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), errorMessage);
return value;
}
// UintToAddressMap
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Library for managing
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
import "./Context.sol";
......@@ -29,7 +29,7 @@ abstract contract Pausable is Context {
/**
* @dev Initializes the contract in unpaused state.
*/
constructor () internal {
constructor () {
_paused = false;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
......@@ -35,7 +35,7 @@ abstract contract ReentrancyGuard {
uint256 private _status;
constructor () internal {
constructor () {
_status = _NOT_ENTERED;
}
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant alphabet = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` representation.
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
......@@ -23,12 +25,43 @@ library Strings {
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
buffer[index--] = bytes1(uint8(48 + temp % 10));
temp /= 10;
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = alphabet[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
......@@ -13,7 +13,4 @@
** xref:erc777.adoc[ERC777]
** xref:erc1155.adoc[ERC1155]
* xref:gsn.adoc[Gas Station Network]
** xref:gsn-strategies.adoc[Strategies]
* xref:utilities.adoc[Utilities]
......@@ -13,7 +13,7 @@ OpenZeppelin provides xref:api:access.adoc#Ownable[`Ownable`] for implementing o
----
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
......@@ -62,7 +62,7 @@ Here's a simple example of using `AccessControl` in an xref:tokens.adoc#ERC20[`E
----
// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
......@@ -71,7 +71,7 @@ contract MyToken is ERC20, AccessControl {
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor(address minter) public ERC20("MyToken", "TKN") {
constructor(address minter) ERC20("MyToken", "TKN") {
// Grant the minter role to a specified account
_setupRole(MINTER_ROLE, minter);
}
......@@ -94,7 +94,7 @@ Let's augment our ERC20 token example by also defining a 'burner' role, which le
----
// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
......@@ -103,7 +103,7 @@ contract MyToken is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor(address minter, address burner) public ERC20("MyToken", "TKN") {
constructor(address minter, address burner) ERC20("MyToken", "TKN") {
_setupRole(MINTER_ROLE, minter);
_setupRole(BURNER_ROLE, burner);
}
......@@ -139,7 +139,7 @@ Let's take a look at the ERC20 token example, this time taking advantage of the
----
// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
......@@ -148,7 +148,7 @@ contract MyToken is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() public ERC20("MyToken", "TKN") {
constructor() ERC20("MyToken", "TKN") {
// Grant the contract deployer the default admin role: it will be able
// to grant and revoke any roles
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
......
......@@ -34,7 +34,7 @@ Here's what a contract for tokenized items might look like:
----
// contracts/GameItems.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
......@@ -45,7 +45,7 @@ contract GameItems is ERC1155 {
uint256 public constant SWORD = 3;
uint256 public constant SHIELD = 4;
constructor() public ERC1155("https://game.example/api/item/{id}.json") {
constructor() ERC1155("https://game.example/api/item/{id}.json") {
_mint(msg.sender, GOLD, 10**18, "");
_mint(msg.sender, SILVER, 10**27, "");
_mint(msg.sender, THORS_HAMMER, 1, "");
......@@ -132,7 +132,7 @@ In order for our contract to receive ERC1155 tokens we can inherit from the conv
----
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
......
......@@ -14,7 +14,7 @@ Let's say we want a token with a fixed supply of 1000, initially allocated to th
[source,solidity]
----
contract ERC20FixedSupply is ERC20 {
constructor() public {
constructor() {
totalSupply += 1000;
balances[msg.sender] += 1000;
}
......@@ -26,7 +26,7 @@ Starting with Contracts v2 this pattern is not only discouraged, but disallowed.
[source,solidity]
----
contract ERC20FixedSupply is ERC20 {
constructor() public ERC20("Fixed", "FIX") {
constructor() ERC20("Fixed", "FIX") {
_mint(msg.sender, 1000);
}
}
......@@ -44,7 +44,7 @@ The mechanism we will implement is a token reward for the miners that produce Et
[source,solidity]
----
contract ERC20WithMinerReward is ERC20 {
constructor() public ERC20("Reward", "RWD") {}
constructor() ERC20("Reward", "RWD") {}
function mintMinerReward() public {
_mint(block.coinbase, 1000);
......@@ -68,7 +68,7 @@ The accounts with the minter role don't need to be externally owned, though, and
contract MinerRewardMinter {
ERC20PresetMinterPauser _token;
constructor(ERC20PresetMinterPauser token) public {
constructor(ERC20PresetMinterPauser token) {
_token = token;
}
......@@ -92,7 +92,7 @@ Adding to the supply mechanism from previous sections, we can use this hook to m
[source,solidity]
----
contract ERC20WithAutoMinerReward is ERC20 {
constructor() public ERC20("Reward", "RWD") {}
constructor() ERC20("Reward", "RWD") {}
function _mintMinerReward() internal {
_mint(block.coinbase, 1000);
......
......@@ -15,12 +15,12 @@ Here's what our GLD token might look like.
----
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract GLDToken is ERC20 {
constructor(uint256 initialSupply) public ERC20("Gold", "GLD") {
constructor(uint256 initialSupply) ERC20("Gold", "GLD") {
_mint(msg.sender, initialSupply);
}
}
......
......@@ -14,7 +14,7 @@ Here's what a contract for tokenized items might look like:
----
// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
......@@ -23,7 +23,7 @@ contract GameItem is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() public ERC721("GameItem", "ITM") {}
constructor() ERC721("GameItem", "ITM") {}
function awardItem(address player, string memory tokenURI)
public
......
......@@ -20,13 +20,12 @@ We will replicate the `GLD` example of the xref:erc20.adoc#constructing-an-erc20
----
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
contract GLDToken is ERC777 {
constructor(uint256 initialSupply, address[] memory defaultOperators)
public
ERC777("Gold", "GLD", defaultOperators)
{
_mint(msg.sender, initialSupply, "", "");
......
......@@ -20,7 +20,7 @@ For example, imagine you want to change xref:api:access.adoc#AccessControl[`Acce
```solidity
// contracts/ModifiedAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
......@@ -48,7 +48,7 @@ Here is a modified version of xref:api:access.adoc#AccessControl[`AccessControl`
```solidity
// contracts/ModifiedAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
......@@ -80,7 +80,7 @@ Hooks are simply functions that are called before or after some action takes pla
Here's how you would implement the `IERC721Receiver` pattern in `ERC20`, using the xref:api:token/ERC20.adoc#ERC20-_beforeTokenTransfer-address-address-uint256-[`_beforeTokenTransfer`] hook:
```solidity
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
......
= GSN Strategies
This guide shows you different strategies to accept relayed calls via the Gas Station Network (GSN) using OpenZeppelin Contracts.
First, we will go over what a 'GSN strategy' is, and then showcase how to use the two most common strategies.
Finally, we will cover how to create your own custom strategies.
If you're still learning about the basics of the Gas Station Network, you should first head over to the xref:gsn.adoc[GSN Guide].
[[gsn-strategies]]
== GSN Strategies Explained
A *GSN strategy* decides which relayed call gets approved and which relayed call gets rejected. Strategies are a key concept within the GSN. Dapps need a strategy to prevent malicious users from spending the dapp's funds for relayed call fees.
As we have seen in the xref:gsn.adoc[GSN Guide], in order to be GSN enabled, your contracts need to extend from xref:api:GSN.adoc#GSNRecipient[`GSNRecipient`].
A GSN recipient contract needs the following to work:
1. It needs to have funds deposited on its RelayHub.
2. It needs to handle `msg.sender` and `msg.data` differently.
3. It needs to decide how to approve and reject relayed calls.
Depositing funds for the GSN recipient contract can be done via the https://gsn.openzeppelin.com/recipients[GSN Dapp tool] or programmatically with xref:gsn-helpers::api.adoc#javascript_interface[*OpenZeppelin GSN Helpers*].
The actual user's `msg.sender` and `msg.data` can be obtained safely via xref:api:GSN.adoc#GSNRecipient-_msgSender--[`_msgSender()`] and xref:api:GSN.adoc#GSNRecipient-_msgData--[`_msgData()`] of xref:api:GSN.adoc#GSNRecipient[`GSNRecipient`].
Deciding how to approve and reject relayed calls is a bit more complex. Chances are you probably want to choose which users can use your contracts via the GSN and potentially charge them for it, like a bouncer at a nightclub. We call these _GSN strategies_.
The base xref:api:GSN.adoc#GSNRecipient[`GSNRecipient`] contract doesn't include a strategy, so you must either use one of the pre-built ones or write your own. We will first go over using the included strategies: xref:api:GSN.adoc#GSNRecipientSignature[`GSNRecipientSignature`] and xref:api:GSN.adoc#GSNRecipientERC20Fee[`GSNRecipientERC20Fee`].
== `GSNRecipientSignature`
xref:api:GSN.adoc#GSNRecipientSignature[`GSNRecipientSignature`] lets users relay calls via the GSN to your recipient contract (charging you for it) if they can prove that an account you trust approved them to do so. The way they do this is via a _signature_.
The relayed call must include a signature of the relayed call parameters by the same account that was added to the contract as a trusted signer. If it is not the same, `GSNRecipientSignature` will not accept the relayed call.
This means that you need to set up a system where your trusted account signs the relayed call parameters to then include in the relayed call, as long as they are valid users (according to your business logic).
The definition of a valid user depends on your system, but an example is users that have completed their sign up via some kind of https://en.wikipedia.org/wiki/OAuth[OAuth] and validation, e.g. gone through a captcha or validated their email address.
You could restrict it further and let new users send a specific number of relayed calls (e.g. limit to 5 relayed calls via the GSN, at which point the user needs to create a wallet).
Alternatively, you could charge the user off-chain (e.g. via credit card) for credit on your system and let them create relayed calls until their credit runs out.
The great thing about this setup is that *your contract doesn't need to change* if you want to change the business rules. All you are doing is changing the backend logic conditions under which users use your contract for free.
On the other hand, you need to have a backend server, microservice, or lambda function to accomplish this.
=== How Does `GSNRecipientSignature` Work?
`GSNRecipientSignature` decides whether or not to accept the relayed call based on the included signature.
The `acceptRelayedCall` implementation recovers the address from the signature of the relayed call parameters in `approvalData` and compares it to the trusted signer.
If the included signature matches the trusted signer, the relayed call is approved.
On the other hand, when the included signature doesn't match the trusted signer, the relayed call gets rejected with an error code of `INVALID_SIGNER`.
=== How to Use `GSNRecipientSignature`
You will need to create an off-chain service (e.g. backend server, microservice, or lambda function) that your dapp calls to sign (or not sign, based on your business logic) the relayed call parameters with your trusted signer account. The signature is then included as the `approvalData` in the relayed call.
Instead of using `GSNRecipient` directly, your GSN recipient contract will instead inherit from `GSNRecipientSignature`, as well as setting the trusted signer in the constructor of `GSNRecipientSignature` as per the following sample code below:
[source,solidity]
----
import "@openzeppelin/contracts/GSN/GSNRecipientSignature.sol";
contract MyContract is GSNRecipientSignature {
constructor(address trustedSigner) public GSNRecipientSignature(trustedSigner) {
}
}
----
TIP: We wrote an in-depth guide on how to setup a signing server that works with `GSNRecipientSignature`, https://forum.openzeppelin.com/t/advanced-gsn-gsnrecipientsignature-sol/1414[check it out!]
== `GSNRecipientERC20Fee`
xref:api:GSN.adoc#GSNRecipientERC20Fee[`GSNRecipientERC20Fee`] is a bit more complex (but don't worry, it has already been written for you!). Unlike `GSNRecipientSignature`, `GSNRecipientERC20Fee` doesn't require any off-chain services.
Instead of off-chain approving each relayed call, you will give special-purpose ERC20 tokens to your users. These tokens are then used as payment for relayed calls to your recipient contract.
Any user that has enough tokens to pay has their relayed calls automatically approved and the recipient contract will cover their transaction costs!
Each recipient contract has their own special-purpose token. The exchange rate from token to ether is 1:1, as the tokens are used to pay your contract to cover the gas fees when using the GSN.
`GSNRecipientERC20Fee` has an internal xref:api:GSN.adoc#GSNRecipientERC20Fee-_mint-address-uint256-[`_mint`] function. Firstly, you need to setup a way to call it (e.g. add a public function with some form of xref:access-control.adoc[access control] such as xref:api:access.adoc#MinterRole-onlyMinter--[`onlyMinter`]).
Then, issue tokens to users based on your business logic. For example, you could mint a limited amount of tokens to new users, mint tokens when users buy them off-chain, give tokens based on a users subscription, etc.
NOTE: *Users do not need to call approve* on their tokens for your recipient contract to use them. They are a modified ERC20 variant that lets the recipient contract retrieve them.
=== How Does `GSNRecipientERC20Fee` Work?
`GSNRecipientERC20Fee` decides to approve or reject relayed calls based on the balance of the users tokens.
The `acceptRelayedCall` function implementation checks the users token balance.
If the user doesn't have enough tokens the relayed call gets rejected with an error of `INSUFFICIENT_BALANCE`.
If there are enough tokens, the relayed call is approved with the end users address, `maxPossibleCharge`, `transactionFee` and `gasPrice` data being returned so it can be used in `_preRelayedCall` and `_postRelayedCall`.
In `_preRelayedCall` function the `maxPossibleCharge` amount of tokens is transferred to the recipient contract.
The maximum amount of tokens required is transferred assuming that the relayed call will use all the gas available.
Then, in the `_postRelayedCall` function, the actual amount is calculated, including the recipient contract implementation and ERC20 token transfers, and the difference is refunded.
The maximum amount of tokens required is transferred in `_preRelayedCall` to protect the contract from exploits (this is really similar to how ether is locked in Ethereum transactions).
NOTE: The gas cost estimation is not 100% accurate, we may tweak it further down the road.
NOTE: Always use `_preRelayedCall` and `_postRelayedCall` functions. Internal `_preRelayedCall` and `_postRelayedCall` functions are used instead of public `preRelayedCall` and `postRelayedCall` functions, as the public functions are prevented from being called by non-RelayHub contracts.
=== How to Use `GSNRecipientERC20Fee`
Your GSN recipient contract needs to inherit from `GSNRecipientERC20Fee` along with appropriate xref:access-control.adoc[access control] (for token minting), set the token details in the constructor of `GSNRecipientERC20Fee` and create a public `mint` function suitably protected by your chosen access control as per the following sample code (which uses xref:api:access.adoc#AccessControl[`AccessControl`]):
[source,solidity]
----
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/GSN/GSNRecipientERC20Fee.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is GSNRecipientERC20Fee, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor() public GSNRecipientERC20Fee("FeeToken", "FEE") {
_setupRole(MINTER_ROLE, _msgSender());
}
function _msgSender() internal view override(Context, GSNRecipient) returns (address payable) {
return GSNRecipient._msgSender();
}
function _msgData() internal view override(Context, GSNRecipient) returns (bytes memory) {
return GSNRecipient._msgData();
}
function mint(address account, uint256 amount) public {
require(hasRole(MINTER_ROLE, _msgSender()), "Caller is not a minter");
_mint(account, amount);
}
}
----
== Custom Strategies
If the included strategies don't quite fit your business needs, you can also write your own! For example, your custom strategy could use a specified token to pay for relayed calls with a custom exchange rate to ether. Alternatively you could issue users who subscribe to your dapp ERC721 tokens, and accounts holding the subscription token could use your contract for free as part of the subscription. There are lots of potential options!
To write a custom strategy, simply inherit from `GSNRecipient` and implement the `acceptRelayedCall`, `_preRelayedCall` and `_postRelayedCall` functions.
Your `acceptRelayedCall` implementation decides whether or not to accept the relayed call: return `_approveRelayedCall` to accept, and return `_rejectRelayedCall` with an error code to reject.
Not all GSN strategies use `_preRelayedCall` and `_postRelayedCall` (though they must still be implemented, e.g. `GSNRecipientSignature` leaves them empty), but are useful when your strategy involves charging end users.
`_preRelayedCall` should take the maximum possible charge, with `_postRelayedCall` refunding any difference from the actual charge once the relayed call has been made.
When returning `_approveRelayedCall` to approve the relayed call, the end users address, `maxPossibleCharge`, `transactionFee` and `gasPrice` data can also be returned so that the data can be used in `_preRelayedCall` and `_postRelayedCall`.
See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/GSN/GSNRecipientERC20Fee.sol[the code for `GSNRecipientERC20Fee`] as an example implementation.
Once your strategy is ready, all your GSN recipient needs to do is inherit from it:
[source,solidity]
----
contract MyContract is MyCustomGSNStrategy {
constructor() public MyCustomGSNStrategy() {
}
}
----
= Writing GSN-capable contracts
The https://gsn.openzeppelin.com[Gas Station Network] allows you to build apps where you pay for your users transactions, so they do not need to hold Ether to pay for gas, easing their onboarding process. In this guide, we will learn how to write smart contracts that can receive transactions from the GSN, by using OpenZeppelin Contracts.
If you're new to the GSN, you probably want to first take a look at the xref:learn::sending-gasless-transactions.adoc[overview of the system] to get a clearer picture of how gasless transactions are achieved. Otherwise, strap in!
== Receiving a Relayed Call
The first step to writing a recipient is to inherit from our GSNRecipient contract. If you're also inheriting from other contracts, such as ERC20, this will work just fine: adding GSNRecipient will make all of your token functions GSN-callable.
```solidity
import "@openzeppelin/contracts/GSN/GSNRecipient.sol";
contract MyContract is GSNRecipient, ... {
}
```
=== `msg.sender` and `msg.data`
There's only one extra detail you need to take care of when working with GSN recipient contracts: _you must never use `msg.sender` or `msg.data` directly_. On relayed calls, `msg.sender` will be `RelayHub` instead of your user! This doesn't mean however you won't be able to retrieve your users' addresses: `GSNRecipient` provides two functions, `_msgSender()` and `_msgData()`, which are drop-in replacements for `msg.sender` and `msg.data` while taking care of the low-level details. As long as you use these two functions instead of the original getters, you're good to go!
WARNING: Third-party contracts you inherit from may not use these replacement functions, making them unsafe to use when mixed with `GSNRecipient`. If in doubt, head on over to our https://forum.openzeppelin.com/c/support[support forum].
=== Accepting and Charging
Unlike regular contract function calls, each relayed call has an additional number of steps it must go through, which are functions of the `IRelayRecipient` interface `RelayHub` will call on your contract. `GSNRecipient` includes this interface but no implementation: most of writing a recipient involves handling these function calls. They are designed to provide flexibility, but basic recipients can safely ignore most of them while still being secure and sound.
The OpenZeppelin Contracts provide a number of tried-and-tested approaches for you to use out of the box, but you should still have a basic idea of what's going on under the hood.
==== `acceptRelayedCall`
First, RelayHub will ask your recipient contract if it wants to receive a relayed call. Recall that you will be charged for incurred gas costs by the relayer, so you should only accept calls that you're willing to pay for!
[source,solidity]
----
function acceptRelayedCall(
address relay,
address from,
bytes calldata encodedFunction,
uint256 transactionFee,
uint256 gasPrice,
uint256 gasLimit,
uint256 nonce,
bytes calldata approvalData,
uint256 maxPossibleCharge
) external view returns (uint256, bytes memory);
----
There are multiple ways to make this work, including:
. having a whitelist of trusted users
. only accepting calls to an onboarding function
. charging users in tokens (possibly issued by you)
. delegating the acceptance logic off-chain
All relayed call requests can be rejected at no cost to the recipient.
In this function, you return a number indicating whether you accept the call (0) or not (any other number). You can also return some arbitrary data that will get passed along to the following two functions (pre and post) as an execution context.
=== pre and postRelayedCall
After a relayed call is accepted, RelayHub will give your contract two opportunities to charge your user for their call, perform some bookkeeping, etc.: _before_ and _after_ the actual relayed call is made. These functions are aptly named `preRelayedCall` and `postRelayedCall`.
[source,solidity]
----
function preRelayedCall(bytes calldata context) external returns (bytes32);
----
`preRelayedCall` will inform you of the maximum cost the call may have, and can be used to charge the user in advance. This is useful if the user may spend their allowance as part of the call, so you can lock some funds here.
[source,solidity]
----
function postRelayedCall(
bytes calldata context,
bool success,
uint256 actualCharge,
bytes32 preRetVal
) external;
----
`postRelayedCall` will give you an accurate estimate of the transaction cost, making it a natural place to charge users. It will also let you know if the relayed call reverted or not. This allows you, for instance, to not charge users for reverted calls - but remember that you will be charged by the relayer nonetheless.
These functions allow you to implement, for instance, a flow where you charge your users for the relayed transactions in a custom token. You can lock some of their tokens in `pre`, and execute the actual charge in `post`. This is similar to how gas fees work in Ethereum: the network first locks enough ETH to pay for the transaction's gas limit at its gas price, and then pays for what it actually spent.
== Further reading
Read our xref:gsn-strategies.adoc[guide on the payment strategies] pre-built and shipped in OpenZeppelin Contracts, or check out xref:api:GSN.adoc[the API reference of the GSN base contracts].
......@@ -5,7 +5,6 @@
* Implementations of standards like xref:erc20.adoc[ERC20] and xref:erc721.adoc[ERC721].
* Flexible xref:access-control.adoc[role-based permissioning] scheme.
* Reusable xref:utilities.adoc[Solidity components] to build custom contracts and complex decentralized systems.
* First-class integration with the xref:gsn.adoc[Gas Station Network] for systems with no gas fees!
* https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/audit[Audited] by leading security firms (_last full audit on v2.0.0_).
== Overview
......@@ -28,12 +27,12 @@ Once installed, you can use the contracts in the library by importing them:
----
// contracts/MyNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MyNFT is ERC721 {
constructor() ERC721("MyNFT", "MNFT") public {
constructor() ERC721("MyNFT", "MNFT") {
}
}
----
......@@ -49,7 +48,6 @@ The guides in the sidebar will teach about different concepts, and how to use th
* xref:access-control.adoc[Access Control]: decide who can perform each of the actions on your system.
* xref:tokens.adoc[Tokens]: create tradable assets or collectibles, like the well known xref:erc20.adoc[ERC20] and xref:erc721.adoc[ERC721] standards.
* xref:gsn.adoc[Gas Station Network]: let your users interact with your contracts without having to pay for gas themselves.
* xref:utilities.adoc[Utilities]: generic useful tools, including non-overflowing math, signature verification, and trustless paying systems.
The xref:api:token/ERC20.adoc[full API] is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts' development in the https://forum.openzeppelin.com[community forum].
......@@ -59,4 +57,3 @@ Finally, you may want to take a look at the https://blog.openzeppelin.com/guides
* https://blog.openzeppelin.com/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05[The Hitchhiker’s Guide to Smart Contracts in Ethereum] will help you get an overview of the various tools available for smart contract development, and help you set up your environment.
* https://blog.openzeppelin.com/a-gentle-introduction-to-ethereum-programming-part-1-783cc7796094[A Gentle Introduction to Ethereum Programming, Part 1] provides very useful information on an introductory level, including many basic concepts from the Ethereum platform.
* For a more in-depth dive, you may read the guide https://blog.openzeppelin.com/designing-the-architecture-for-your-ethereum-application-9cec086f8317[Designing the architecture for your Ethereum application], which discusses how to better structure your application and its relationship to the real world.
......@@ -16,7 +16,7 @@ The data signer can be recovered with xref:api:cryptography.adoc#ECDSA-recover-b
using ECDSA for bytes32;
function _verify(bytes32 data, address account) pure returns (bool) {
return keccack256(data)
return keccak256(data)
.toEthSignedMessageHash()
.recover(signature) == account;
}
......
const fs = require('fs');
const path = require('path');
require('@nomiclabs/hardhat-truffle5');
require('@nomiclabs/hardhat-solhint');
require('solidity-coverage');
require('hardhat-gas-reporter');
for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) {
require(path.join(__dirname, 'hardhat', f));
}
const enableGasReport = !!process.env.ENABLE_GAS_REPORT;
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: {
version: '0.8.0',
settings: {
optimizer: {
enabled: enableGasReport,
runs: 200,
},
},
},
networks: {
hardhat: {
blockGasLimit: 10000000,
},
},
gasReporter: {
enable: enableGasReport,
currency: 'USD',
outputFile: process.env.CI ? 'gas-report.txt' : undefined,
},
};
const { BuidlerError } = require('@nomiclabs/buidler/internal/core/errors');
const { HardhatError } = require('hardhat/internal/core/errors');
extendEnvironment(env => {
const { artifacts } = env;
const artifactsRequire = env.artifacts.require;
env.artifacts = {
...artifacts,
require (name) {
for (const suffix of ['UpgradeableWithInit', 'Upgradeable', '']) {
try {
return artifacts.require(name + suffix);
} catch (e) {
if (BuidlerError.isBuidlerError(e) && e.number === 700 && suffix !== '') {
continue;
} else {
throw e;
}
env.artifacts.require = (name) => {
for (const suffix of ['UpgradeableWithInit', 'Upgradeable', '']) {
try {
return artifactsRequire(name + suffix);
} catch (e) {
if (HardhatError.isHardhatError(e) && e.number === 700 && suffix !== '') {
continue;
} else {
throw e;
}
}
throw new Error('Unreachable');
},
}
throw new Error('Unreachable');
};
});
// adds storageLayout to solc outputSelection, necessary for storage gaps
const { internalTask } = require('@nomiclabs/buidler/config');
const { TASK_COMPILE_GET_COMPILER_INPUT } = require('@nomiclabs/buidler/builtin-tasks/task-names');
const { internalTask } = require('hardhat/config');
const { TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT } = require('hardhat/builtin-tasks/task-names');
internalTask(TASK_COMPILE_GET_COMPILER_INPUT, async (args, bre, runSuper) => {
internalTask(TASK_COMPILE_SOLIDITY_GET_COMPILER_INPUT, async (args, bre, runSuper) => {
const input = await runSuper();
input.settings.outputSelection['*']['*'].push('storageLayout');
return input;
......
// ignores the proxy tests
const { internalTask } = require('@nomiclabs/buidler/config');
const { internalTask } = require('hardhat/config');
const { TASK_TEST_GET_TEST_FILES } = require('@nomiclabs/buidler/builtin-tasks/task-names');
const { TASK_TEST_GET_TEST_FILES } = require('hardhat/builtin-tasks/task-names');
const glob = require('glob');
const path = require('path');
const { promisify } = require('util');
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
"private": true,
"name": "openzeppelin-solidity",
"description": "Secure Smart Contract library for Solidity",
"version": "3.3.0",
"version": "3.4.0",
"files": [
"/contracts/**/*.sol",
"/build/contracts/*.json",
......@@ -11,8 +11,8 @@
"/test/behaviors"
],
"scripts": {
"compile": "buidler compile",
"coverage": "buidler coverage",
"compile": "hardhat compile",
"coverage": "hardhat coverage",
"docs": "oz-docs",
"docs:watch": "npm run docs watch contracts 'docs/*.hbs'",
"prepare-docs": "scripts/prepare-docs.sh",
......@@ -20,13 +20,14 @@
"lint:fix": "npm run lint:js:fix",
"lint:js": "eslint --ignore-path .gitignore .",
"lint:js:fix": "eslint --ignore-path .gitignore . --fix",
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
"lint:sol": "echo 'solidity linter currently disabled' # solhint --max-warnings 0 \"contracts/**/*.sol\"",
"prepublish": "rimraf build contracts/build artifacts cache",
"prepare": "npm run compile",
"prepack": "scripts/prepack.sh",
"release": "scripts/release/release.sh",
"version": "scripts/release/version.sh",
"test": "buidler test"
"test": "hardhat test",
"gas-report": "env ENABLE_GAS_REPORT=1 npm run test"
},
"repository": {
"type": "git",
......@@ -47,12 +48,10 @@
},
"homepage": "https://openzeppelin.com/contracts/",
"devDependencies": {
"@nomiclabs/buidler": "^1.4.8",
"@nomiclabs/buidler-truffle5": "^1.3.4",
"@nomiclabs/buidler-web3": "^1.3.4",
"@nomiclabs/hardhat-solhint": "^2.0.0",
"@nomiclabs/hardhat-truffle5": "^2.0.0",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/docs-utils": "^0.1.0",
"@openzeppelin/gsn-helpers": "^0.2.3",
"@openzeppelin/gsn-provider": "^0.1.10",
"@openzeppelin/test-helpers": "^0.5.9",
"chai": "^4.2.0",
"eslint": "^6.5.1",
......@@ -65,6 +64,8 @@
"eth-sig-util": "^3.0.0",
"ethereumjs-util": "^7.0.7",
"ethereumjs-wallet": "^1.0.1",
"hardhat": "^2.0.6",
"hardhat-gas-reporter": "^1.0.4",
"lodash.startcase": "^4.4.0",
"lodash.zip": "^4.2.0",
"micromatch": "^4.0.2",
......
const path = require('path');
const bre = require('@nomiclabs/buidler');
const hre = require('hardhat');
const { Compiler } = require('@nomiclabs/buidler/internal/solidity/compiler');
const { getCompilersDir } = require('hardhat/internal/util/global-dir');
const { CompilerDownloader } = require('hardhat/internal/solidity/compiler/downloader');
const { Compiler } = require('hardhat/internal/solidity/compiler');
const compiler = new Compiler(
bre.config.solc.version,
path.join(bre.config.paths.cache, 'compilers'),
);
const [{ version }] = hre.config.solidity.compilers;
module.exports = Object.assign(compiler.getSolc(), { __esModule: true });
async function getSolc () {
const downloader = new CompilerDownloader(await getCompilersDir(), { forceSolcJs: true });
const { compilerPath } = await downloader.getDownloadedCompilerPath(version);
const compiler = new Compiler(compilerPath);
return compiler.getSolc();
}
module.exports = Object.assign(getSolc(), { __esModule: true });
const { constants, expectEvent } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3');
const ERC721GSNRecipientMock = artifacts.require('ERC721GSNRecipientMock');
contract('ERC721GSNRecipient (integration)', function (accounts) {
const [ signer, sender ] = accounts;
const name = 'Non Fungible Token';
const symbol = 'NFT';
const tokenId = '42';
before(function () {
setGSNProvider(ERC721GSNRecipientMock, accounts);
});
beforeEach(async function () {
this.token = await ERC721GSNRecipientMock.new(name, symbol, signer);
});
async function testMintToken (token, from, tokenId, options = {}) {
const { tx } = await token.mint(tokenId, { from, ...options });
await expectEvent.inTransaction(tx, ERC721GSNRecipientMock, 'Transfer', { from: ZERO_ADDRESS, to: from, tokenId });
}
context('when called directly', function () {
it('sender can mint tokens', async function () {
await testMintToken(this.token, sender, tokenId);
});
});
context('when relay-called', function () {
beforeEach(async function () {
await gsn.fundRecipient(web3, { recipient: this.token.address });
});
it('sender can mint tokens', async function () {
const approveFunction = async (data) =>
fixSignature(
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, this.token.address,
), signer,
),
);
await testMintToken(this.token, sender, tokenId, { useGSN: true, approveFunction });
});
});
});
const { balance, BN, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants;
const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai');
const GSNRecipientMock = artifacts.require('GSNRecipientMock');
const ContextMockCaller = artifacts.require('ContextMockCaller');
const { shouldBehaveLikeRegularContext } = require('./Context.behavior');
contract('GSNRecipient', function (accounts) {
const [ payee, sender, newRelayHub ] = accounts;
before(function () {
setGSNProvider(GSNRecipientMock, accounts);
setGSNProvider(ContextMockCaller, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientMock.new();
});
it('returns the compatible RelayHub version', async function () {
expect(await this.recipient.relayHubVersion()).to.equal('1.0.0');
});
describe('get/set RelayHub', function () {
const singletonRelayHub = '0xD216153c06E857cD7f72665E0aF1d7D82172F494';
it('initially returns the singleton instance address', async function () {
expect(await this.recipient.getHubAddr()).to.equal(singletonRelayHub);
});
it('can be upgraded to a new RelayHub', async function () {
const { logs } = await this.recipient.upgradeRelayHub(newRelayHub);
expectEvent.inLogs(logs, 'RelayHubChanged', { oldRelayHub: singletonRelayHub, newRelayHub });
});
it('cannot upgrade to the same RelayHub', async function () {
await expectRevert(
this.recipient.upgradeRelayHub(singletonRelayHub),
'GSNRecipient: new RelayHub is the current one',
);
});
it('cannot upgrade to the zero address', async function () {
await expectRevert(
this.recipient.upgradeRelayHub(ZERO_ADDRESS), 'GSNRecipient: new RelayHub is the zero address',
);
});
context('with new RelayHub', function () {
beforeEach(async function () {
await this.recipient.upgradeRelayHub(newRelayHub);
});
it('returns the new instance address', async function () {
expect(await this.recipient.getHubAddr()).to.equal(newRelayHub);
});
});
});
context('when called directly', function () {
beforeEach(async function () {
this.context = this.recipient; // The Context behavior expects the contract in this.context
this.caller = await ContextMockCaller.new();
});
shouldBehaveLikeRegularContext(sender);
});
context('when receiving a relayed call', function () {
beforeEach(async function () {
await gsn.fundRecipient(web3, { recipient: this.recipient.address });
});
describe('msgSender', function () {
it('returns the relayed transaction original sender', async function () {
const { tx } = await this.recipient.msgSender({ from: sender, useGSN: true });
await expectEvent.inTransaction(tx, GSNRecipientMock, 'Sender', { sender });
});
});
describe('msgData', function () {
it('returns the relayed transaction original data', async function () {
const integerValue = new BN('42');
const stringValue = 'OpenZeppelin';
const callData = this.recipient.contract.methods.msgData(integerValue.toString(), stringValue).encodeABI();
// The provider doesn't properly estimate gas for a relayed call, so we need to manually set a higher value
const { tx } = await this.recipient.msgData(integerValue, stringValue, { gas: 1000000, useGSN: true });
await expectEvent.inTransaction(tx, GSNRecipientMock, 'Data', { data: callData, integerValue, stringValue });
});
});
});
context('with deposited funds', async function () {
const amount = ether('1');
beforeEach(async function () {
await gsn.fundRecipient(web3, { recipient: this.recipient.address, amount });
});
it('funds can be withdrawn', async function () {
const balanceTracker = await balance.tracker(payee);
await this.recipient.withdrawDeposits(amount, payee);
expect(await balanceTracker.delta()).to.be.bignumber.equal(amount);
});
it('partial funds can be withdrawn', async function () {
const balanceTracker = await balance.tracker(payee);
await this.recipient.withdrawDeposits(amount.divn(2), payee);
expect(await balanceTracker.delta()).to.be.bignumber.equal(amount.divn(2));
});
it('reverts on overwithdrawals', async function () {
await expectRevert(this.recipient.withdrawDeposits(amount.addn(1), payee), 'insufficient funds');
});
});
});
const { ether, expectEvent } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { expect } = require('chai');
const GSNRecipientERC20FeeMock = artifacts.require('GSNRecipientERC20FeeMock');
const ERC20 = artifacts.require('ERC20');
const IRelayHub = artifacts.require('IRelayHub');
contract('GSNRecipientERC20Fee', function (accounts) {
const [ sender ] = accounts;
const name = 'FeeToken';
const symbol = 'FTKN';
before(function () {
setGSNProvider(GSNRecipientERC20FeeMock, accounts);
setGSNProvider(ERC20, accounts);
setGSNProvider(IRelayHub, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientERC20FeeMock.new(name, symbol);
this.token = await ERC20.at(await this.recipient.token());
});
describe('token', function () {
it('has a name', async function () {
expect(await this.token.name()).to.equal(name);
});
it('has a symbol', async function () {
expect(await this.token.symbol()).to.equal(symbol);
});
it('has 18 decimals', async function () {
expect(await this.token.decimals()).to.be.bignumber.equal('18');
});
});
context('when called directly', function () {
it('mock function can be called', async function () {
const { logs } = await this.recipient.mockFunction();
expectEvent.inLogs(logs, 'MockFunctionCalled');
});
});
context('when relay-called', function () {
beforeEach(async function () {
await gsn.fundRecipient(web3, { recipient: this.recipient.address });
this.relayHub = await IRelayHub.at('0xD216153c06E857cD7f72665E0aF1d7D82172F494');
});
it('charges the sender for GSN fees in tokens', async function () {
// The recipient will be charged from its RelayHub balance, and in turn charge the sender from its sender balance.
// Both amounts should be roughly equal.
// The sender has a balance in tokens, not ether, but since the exchange rate is 1:1, this works fine.
const senderPreBalance = ether('2');
await this.recipient.mint(sender, senderPreBalance);
const recipientPreBalance = await this.relayHub.balanceOf(this.recipient.address);
const { tx } = await this.recipient.mockFunction({ from: sender, useGSN: true });
await expectEvent.inTransaction(tx, IRelayHub, 'TransactionRelayed', { status: '0' });
const senderPostBalance = await this.token.balanceOf(sender);
const recipientPostBalance = await this.relayHub.balanceOf(this.recipient.address);
const senderCharge = senderPreBalance.sub(senderPostBalance);
const recipientCharge = recipientPreBalance.sub(recipientPostBalance);
expect(senderCharge).to.be.bignumber.closeTo(recipientCharge, recipientCharge.divn(10));
});
});
});
const { expectEvent, expectRevert, constants } = require('@openzeppelin/test-helpers');
const gsn = require('@openzeppelin/gsn-helpers');
const { fixSignature } = require('../helpers/sign');
const { setGSNProvider } = require('../helpers/set-gsn-provider');
const { utils: { toBN } } = require('web3');
const { ZERO_ADDRESS } = constants;
const GSNRecipientSignatureMock = artifacts.require('GSNRecipientSignatureMock');
contract('GSNRecipientSignature', function (accounts) {
const [ signer, other ] = accounts;
before(function () {
setGSNProvider(GSNRecipientSignatureMock, accounts);
});
beforeEach(async function () {
this.recipient = await GSNRecipientSignatureMock.new(signer);
});
context('when called directly', function () {
it('mock function can be called', async function () {
const { logs } = await this.recipient.mockFunction();
expectEvent.inLogs(logs, 'MockFunctionCalled');
});
});
context('when constructor is called with a zero address', function () {
it('fails when constructor called with a zero address', async function () {
await expectRevert(
GSNRecipientSignatureMock.new(
ZERO_ADDRESS,
),
'GSNRecipientSignature: trusted signer is the zero address',
);
});
});
context('when relay-called', function () {
beforeEach(async function () {
await gsn.fundRecipient(web3, { recipient: this.recipient.address });
});
it('rejects unsigned relay requests', async function () {
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true }));
});
it('rejects relay requests where some parameters are signed', async function () {
const approveFunction = async (data) =>
fixSignature(
await web3.eth.sign(
web3.utils.soliditySha3(
// the nonce is not signed
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas),
), signer,
),
);
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));
});
it('accepts relay requests where all parameters are signed', async function () {
const approveFunction = async (data) =>
fixSignature(
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), signer,
),
);
const { tx } = await this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction });
await expectEvent.inTransaction(tx, GSNRecipientSignatureMock, 'MockFunctionCalled');
});
it('rejects relay requests where all parameters are signed by an invalid signer', async function () {
const approveFunction = async (data) =>
fixSignature(
await web3.eth.sign(
web3.utils.soliditySha3(
// eslint-disable-next-line max-len
data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, data.to,
), other,
),
);
await gsn.expectError(this.recipient.mockFunction({ value: 0, useGSN: true, approveFunction }));
});
});
});
const { GSNDevProvider } = require('@openzeppelin/gsn-provider');
function setGSNProvider (Contract, accounts) {
const baseProvider = Contract.currentProvider;
Contract.setProvider(
new GSNDevProvider(baseProvider, {
txfee: 70,
useGSN: false,
ownerAddress: accounts[8],
relayerAddress: accounts[9],
}),
);
};
module.exports = { setGSNProvider };
const { expectRevert } = require('@openzeppelin/test-helpers');
const { shouldSupportInterfaces } = require('./SupportsInterface.behavior');
const ERC165Mock = artifacts.require('ERC165Mock');
......@@ -9,10 +7,6 @@ contract('ERC165', function (accounts) {
this.mock = await ERC165Mock.new();
});
it('does not allow 0xffffffff', async function () {
await expectRevert(this.mock.registerInterface('0xffffffff'), 'ERC165: invalid interface id');
});
shouldSupportInterfaces([
'ERC165',
]);
......
......@@ -3,6 +3,7 @@ require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const ERC165CheckerMock = artifacts.require('ERC165CheckerMock');
const ERC165MissingData = artifacts.require('ERC165MissingData');
const ERC165NotSupported = artifacts.require('ERC165NotSupported');
const ERC165InterfacesSupported = artifacts.require('ERC165InterfacesSupported');
......@@ -18,6 +19,33 @@ contract('ERC165Checker', function (accounts) {
this.mock = await ERC165CheckerMock.new();
});
context('ERC165 missing return data', function () {
beforeEach(async function () {
this.target = await ERC165MissingData.new();
});
it('does not support ERC165', async function () {
const supported = await this.mock.supportsERC165(this.target.address);
expect(supported).to.equal(false);
});
it('does not support mock interface via supportsInterface', async function () {
const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID);
expect(supported).to.equal(false);
});
it('does not support mock interface via supportsAllInterfaces', async function () {
const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]);
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 not supported', function () {
beforeEach(async function () {
this.target = await ERC165NotSupported.new();
......
const { expectRevert } = require('@openzeppelin/test-helpers');
const { shouldSupportInterfaces } = require('./SupportsInterface.behavior');
const ERC165Mock = artifacts.require('ERC165StorageMock');
contract('ERC165Storage', function (accounts) {
beforeEach(async function () {
this.mock = await ERC165Mock.new();
});
it('register interface', async function () {
expect(await this.mock.supportsInterface('0x00000001')).to.be.equal(false);
await this.mock.registerInterface('0x00000001');
expect(await this.mock.supportsInterface('0x00000001')).to.be.equal(true);
});
it('does not allow 0xffffffff', async function () {
await expectRevert(this.mock.registerInterface('0xffffffff'), 'ERC165: invalid interface id');
});
shouldSupportInterfaces([
'ERC165',
]);
});
......@@ -26,8 +26,13 @@ contract('SafeMath', function (accounts) {
}
async function testFailsCommutative (fn, lhs, rhs, reason, ...extra) {
await expectRevert(fn(lhs, rhs, ...extra), reason);
await expectRevert(fn(rhs, lhs, ...extra), reason);
if (reason === undefined) {
await expectRevert.unspecified(fn(lhs, rhs, ...extra));
await expectRevert.unspecified(fn(rhs, lhs, ...extra));
} else {
await expectRevert(fn(lhs, rhs, ...extra), reason);
await expectRevert(fn(rhs, lhs, ...extra), reason);
}
}
async function testCommutativeIterable (fn, lhs, rhs, expected, ...extra) {
......@@ -174,7 +179,7 @@ contract('SafeMath', function (accounts) {
const a = MAX_UINT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.doAdd, a, b, 'SafeMath: addition overflow');
await testFailsCommutative(this.safeMath.doAdd, a, b, undefined);
});
});
......@@ -190,7 +195,7 @@ contract('SafeMath', function (accounts) {
const a = new BN('1234');
const b = new BN('5678');
await expectRevert(this.safeMath.doSub(a, b), 'SafeMath: subtraction overflow');
await expectRevert.unspecified(this.safeMath.doSub(a, b));
});
});
......@@ -213,7 +218,7 @@ contract('SafeMath', function (accounts) {
const a = MAX_UINT256;
const b = new BN('2');
await testFailsCommutative(this.safeMath.doMul, a, b, 'SafeMath: multiplication overflow');
await testFailsCommutative(this.safeMath.doMul, a, b, undefined);
});
});
......@@ -243,7 +248,7 @@ contract('SafeMath', function (accounts) {
const a = new BN('5678');
const b = new BN('0');
await expectRevert(this.safeMath.doDiv(a, b), 'SafeMath: division by zero');
await expectRevert.unspecified(this.safeMath.doDiv(a, b));
});
});
......@@ -282,7 +287,7 @@ contract('SafeMath', function (accounts) {
const a = new BN('5678');
const b = new BN('0');
await expectRevert(this.safeMath.doMod(a, b), 'SafeMath: modulo by zero');
await expectRevert.unspecified(this.safeMath.doMod(a, b));
});
});
});
......
......@@ -15,9 +15,9 @@ contract('SignedSafeMath', function (accounts) {
expect(await fn(rhs, lhs)).to.be.bignumber.equal(expected);
}
async function testFailsCommutative (fn, lhs, rhs, reason) {
await expectRevert(fn(lhs, rhs), reason);
await expectRevert(fn(rhs, lhs), reason);
async function testFailsCommutative (fn, lhs, rhs) {
await expectRevert.unspecified(fn(lhs, rhs));
await expectRevert.unspecified(fn(rhs, lhs));
}
describe('add', function () {
......@@ -39,14 +39,14 @@ contract('SignedSafeMath', function (accounts) {
const a = MAX_INT256;
const b = new BN('1');
await testFailsCommutative(this.safeMath.add, a, b, 'SignedSafeMath: addition overflow');
await testFailsCommutative(this.safeMath.add, a, b);
});
it('reverts on negative addition overflow', async function () {
const a = MIN_INT256;
const b = new BN('-1');
await testFailsCommutative(this.safeMath.add, a, b, 'SignedSafeMath: addition overflow');
await testFailsCommutative(this.safeMath.add, a, b);
});
});
......@@ -71,14 +71,14 @@ contract('SignedSafeMath', function (accounts) {
const a = MAX_INT256;
const b = new BN('-1');
await expectRevert(this.safeMath.sub(a, b), 'SignedSafeMath: subtraction overflow');
await expectRevert.unspecified(this.safeMath.sub(a, b));
});
it('reverts on negative subtraction overflow', async function () {
const a = MIN_INT256;
const b = new BN('1');
await expectRevert(this.safeMath.sub(a, b), 'SignedSafeMath: subtraction overflow');
await expectRevert.unspecified(this.safeMath.sub(a, b));
});
});
......@@ -101,14 +101,14 @@ contract('SignedSafeMath', function (accounts) {
const a = MAX_INT256;
const b = new BN('2');
await testFailsCommutative(this.safeMath.mul, a, b, 'SignedSafeMath: multiplication overflow');
await testFailsCommutative(this.safeMath.mul, a, b);
});
it('reverts when minimum integer is multiplied by -1', async function () {
const a = MIN_INT256;
const b = new BN('-1');
await testFailsCommutative(this.safeMath.mul, a, b, 'SignedSafeMath: multiplication overflow');
await testFailsCommutative(this.safeMath.mul, a, b);
});
});
......@@ -139,14 +139,14 @@ contract('SignedSafeMath', function (accounts) {
const a = new BN('-5678');
const b = new BN('0');
await expectRevert(this.safeMath.div(a, b), 'SignedSafeMath: division by zero');
await expectRevert.unspecified(this.safeMath.div(a, b));
});
it('reverts on overflow, negative second', async function () {
const a = new BN(MIN_INT256);
const b = new BN('-1');
await expectRevert(this.safeMath.div(a, b), 'SignedSafeMath: division overflow');
await expectRevert.unspecified(this.safeMath.div(a, b));
});
});
});
......@@ -12,7 +12,7 @@ const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl');
const BadBeaconNotContract = artifacts.require('BadBeaconNotContract');
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0').substr(-40));
}
const BEACON_LABEL = 'eip1967.proxy.beacon';
......
......@@ -20,7 +20,7 @@ const IMPLEMENTATION_LABEL = 'eip1967.proxy.implementation';
const ADMIN_LABEL = 'eip1967.proxy.admin';
function toChecksumAddress (address) {
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0'));
return ethereumjsUtil.toChecksumAddress('0x' + address.replace(/^0x/, '').padStart(40, '0').substr(-40));
}
module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) {
......
......@@ -29,7 +29,7 @@ module.exports = function shouldBehaveLikeUpgradeableProxy (createProxy, proxyAd
const assertProxyInitialization = function ({ value, balance }) {
it('sets the implementation address', async function () {
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)).substr(-40));
expect(implementation).to.be.equal(this.implementation);
});
......
// a different comment!
const { deployRelayHub } = require('@openzeppelin/gsn-helpers');
before('deploy GSN RelayHub', async function () {
const [defaultSender] = await web3.eth.getAccounts();
await deployRelayHub(web3, { from: defaultSender });
});
......@@ -8,6 +8,9 @@ const { shouldSupportInterfaces } = require('../../introspection/SupportsInterfa
const ERC721Mock = artifacts.require('ERC721Mock');
const ERC721ReceiverMock = artifacts.require('ERC721ReceiverMock');
const Error = [ 'None', 'RevertWithMessage', 'RevertWithoutMessage', 'Panic' ]
.reduce((acc, entry, idx) => Object.assign({ [entry]: idx }, acc), {});
contract('ERC721', function (accounts) {
const [owner, newOwner, approved, anotherApproved, operator, other] = accounts;
......@@ -324,7 +327,7 @@ contract('ERC721', function (accounts) {
describe('to a valid receiver contract', function () {
beforeEach(async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, false);
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
this.toWhom = this.receiver.address;
});
......@@ -379,7 +382,7 @@ contract('ERC721', function (accounts) {
describe('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
......@@ -387,9 +390,9 @@ contract('ERC721', function (accounts) {
});
});
describe('to a receiver contract that throws', function () {
describe('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721ReceiverMock: reverting',
......@@ -397,6 +400,25 @@ contract('ERC721', function (accounts) {
});
});
describe('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
describe('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }),
);
});
});
describe('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
......@@ -416,7 +438,7 @@ contract('ERC721', function (accounts) {
describe('via safeMint', function () { // regular minting is tested in ERC721Mintable.test.js and others
it('calls onERC721Received — with data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, false);
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId, data);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
......@@ -427,7 +449,7 @@ contract('ERC721', function (accounts) {
});
it('calls onERC721Received — without data', async function () {
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, false);
this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None);
const receipt = await this.token.safeMint(this.receiver.address, tokenId);
await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', {
......@@ -438,7 +460,7 @@ contract('ERC721', function (accounts) {
context('to a receiver contract returning unexpected value', function () {
it('reverts', async function () {
const invalidReceiver = await ERC721ReceiverMock.new('0x42', false);
const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None);
await expectRevert(
this.token.safeMint(invalidReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
......@@ -446,9 +468,9 @@ contract('ERC721', function (accounts) {
});
});
context('to a receiver contract that throws', function () {
context('to a receiver contract that reverts with message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, true);
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721ReceiverMock: reverting',
......@@ -456,6 +478,25 @@ contract('ERC721', function (accounts) {
});
});
context('to a receiver contract that reverts without message', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage);
await expectRevert(
this.token.safeMint(revertingReceiver.address, tokenId),
'ERC721: transfer to non ERC721Receiver implementer',
);
});
});
context('to a receiver contract that panics', function () {
it('reverts', async function () {
const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic);
await expectRevert.unspecified(
this.token.safeMint(revertingReceiver.address, tokenId),
);
});
});
context('to a contract that does not implement the required function', function () {
it('reverts', async function () {
const nonReceiver = this.token;
......@@ -760,7 +801,7 @@ contract('ERC721', function (accounts) {
it('reverts if index is greater than supply', async function () {
await expectRevert(
this.token.tokenByIndex(2), 'EnumerableMap: index out of bounds',
this.token.tokenByIndex(2), 'EnumerableSet: index out of bounds',
);
});
......@@ -867,7 +908,7 @@ contract('ERC721', function (accounts) {
await this.token.burn(secondTokenId, { from: owner });
expect(await this.token.totalSupply()).to.be.bignumber.equal('0');
await expectRevert(
this.token.tokenByIndex(0), 'EnumerableMap: index out of bounds',
this.token.tokenByIndex(0), 'EnumerableSet: index out of bounds',
);
});
......
......@@ -143,7 +143,7 @@ contract('Address', function (accounts) {
}, []);
await expectRevert(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '90000' }),
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '100000' }),
'Address: low-level call failed',
);
});
......@@ -155,9 +155,8 @@ contract('Address', function (accounts) {
inputs: [],
}, []);
await expectRevert(
await expectRevert.unspecified(
this.mock.functionCall(this.contractRecipient.address, abiEncodedCall),
'Address: low-level call failed',
);
});
......
......@@ -43,7 +43,7 @@ contract('Counters', function (accounts) {
it('reverts if the current value is 0', async function () {
await this.counter.decrement();
await expectRevert(this.counter.decrement(), 'SafeMath: subtraction overflow');
await expectRevert(this.counter.decrement(), 'Counter: decrement overflow');
});
});
context('after incremented to 3', function () {
......
const { constants } = require('@openzeppelin/test-helpers');
const { constants, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
......@@ -9,7 +9,7 @@ contract('Strings', function (accounts) {
this.strings = await StringsMock.new();
});
describe('from uint256', function () {
describe('from uint256 - decimal format', function () {
it('converts 0', async function () {
expect(await this.strings.fromUint256(0)).to.equal('0');
});
......@@ -22,4 +22,38 @@ contract('Strings', function (accounts) {
expect(await this.strings.fromUint256(constants.MAX_UINT256)).to.equal(constants.MAX_UINT256.toString());
});
});
describe('from uint256 - hex format', function () {
it('converts 0', async function () {
expect(await this.strings.fromUint256Hex(0)).to.equal('0x00');
});
it('converts a positive number', async function () {
expect(await this.strings.fromUint256Hex(0x4132)).to.equal('0x4132');
});
it('converts MAX_UINT256', async function () {
expect(await this.strings.fromUint256Hex(constants.MAX_UINT256))
.to.equal(web3.utils.toHex(constants.MAX_UINT256));
});
});
describe('from uint256 - fixed hex format', function () {
it('converts a positive number (long)', async function () {
expect(await this.strings.fromUint256HexFixed(0x4132, 32))
.to.equal('0x0000000000000000000000000000000000000000000000000000000000004132');
});
it('converts a positive number (short)', async function () {
await expectRevert(
this.strings.fromUint256HexFixed(0x4132, 1),
'Strings: hex length insufficient',
);
});
it('converts MAX_UINT256', async function () {
expect(await this.strings.fromUint256HexFixed(constants.MAX_UINT256, 32))
.to.equal(web3.utils.toHex(constants.MAX_UINT256));
});
});
});
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