Merge branch 'master' into release-v2.4.0

This commit is contained in:
Francisco Giordano
2019-08-16 21:25:51 +02:00
20 changed files with 134 additions and 386 deletions

View File

@ -11,36 +11,22 @@ import "./Context.sol";
* recipient contract: end users should use `GSNRecipient` instead.
*/
contract GSNContext is Context {
// We use a random storage slot to allow proxy contracts to enable GSN support in an upgrade without changing their
// storage layout. This value is calculated as: keccak256('gsn.relayhub.address'), minus 1.
bytes32 private constant RELAY_HUB_ADDRESS_STORAGE_SLOT = 0x06b7792c761dcc05af1761f0315ce8b01ac39c16cc934eb0b2f7a8e71414f262;
address internal _relayHub = 0xD216153c06E857cD7f72665E0aF1d7D82172F494;
event RelayHubChanged(address indexed oldRelayHub, address indexed newRelayHub);
constructor() internal {
_upgradeRelayHub(0xD216153c06E857cD7f72665E0aF1d7D82172F494);
}
function _getRelayHub() internal view returns (address relayHub) {
bytes32 slot = RELAY_HUB_ADDRESS_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
relayHub := sload(slot)
}
// solhint-disable-previous-line no-empty-blocks
}
function _upgradeRelayHub(address newRelayHub) internal {
address currentRelayHub = _getRelayHub();
address currentRelayHub = _relayHub;
require(newRelayHub != address(0), "GSNContext: new RelayHub is the zero address");
require(newRelayHub != currentRelayHub, "GSNContext: new RelayHub is the current one");
emit RelayHubChanged(currentRelayHub, newRelayHub);
bytes32 slot = RELAY_HUB_ADDRESS_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(slot, newRelayHub)
}
_relayHub = newRelayHub;
}
// Overrides for Context's functions: when called from RelayHub, sender and
@ -49,7 +35,7 @@ contract GSNContext is Context {
// when handling said data.
function _msgSender() internal view returns (address) {
if (msg.sender != _getRelayHub()) {
if (msg.sender != _relayHub) {
return msg.sender;
} else {
return _getRelayedCallSender();
@ -57,7 +43,7 @@ contract GSNContext is Context {
}
function _msgData() internal view returns (bytes memory) {
if (msg.sender != _getRelayHub()) {
if (msg.sender != _relayHub) {
return msg.data;
} else {
return _getRelayedCallData();

View File

@ -11,10 +11,18 @@ import "./IRelayHub.sol";
* must do so themselves.
*/
contract GSNRecipient is IRelayRecipient, GSNContext, GSNBouncerBase {
/**
* @dev Returns the RelayHub address for this recipient contract.
*/
function getHubAddr() public view returns (address) {
return _getRelayHub();
return _relayHub;
}
/**
* @dev This function returns the version string of the RelayHub for which
* this recipient implementation was built. It's not currently used, but
* may be used by tooling.
*/
// This function is view for future-proofing, it may require reading from
// storage in the future.
function relayHubVersion() public view returns (string memory) {
@ -22,7 +30,12 @@ contract GSNRecipient is IRelayRecipient, GSNContext, GSNBouncerBase {
return "1.0.0";
}
/**
* @dev Triggers a withdraw of the recipient's deposits in RelayHub. Can
* be used by derived contracts to expose the functionality in an external
* interface.
*/
function _withdrawDeposits(uint256 amount, address payable payee) internal {
IRelayHub(_getRelayHub()).withdraw(amount, payee);
IRelayHub(_relayHub).withdraw(amount, payee);
}
}

View File

@ -1,4 +1,8 @@
= GSN
= Gas Station Network
NOTE: This feature is being released in the next version of OpenZeppelin Contracts, available right now through `npm install @openzeppelin/contracts@next`.
TIP: Check out our guide on the xref:ROOT:gsn.adoc[basics of the GSN] as well as the xref:ROOT:gsn-advanced.adoc[more advanced topics].
== Recipient

View File

@ -7,6 +7,15 @@ import "../../token/ERC20/SafeERC20.sol";
import "../../token/ERC20/ERC20.sol";
import "../../token/ERC20/ERC20Detailed.sol";
/**
* @dev A xref:ROOT:gsn-advanced.adoc#gsn-bouncers[GSN Bouncer] 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 GSNBouncerERC20Fee is GSNBouncerBase {
using SafeERC20 for __unstable__ERC20PrimaryAdmin;
using SafeMath for uint256;
@ -17,18 +26,31 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
__unstable__ERC20PrimaryAdmin private _token;
/**
* @dev The arguments to the constructor are the details that the gas payment token will have: `name`, `symbol`, and
* `decimals`.
*/
constructor(string memory name, string memory symbol, uint8 decimals) public {
_token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
}
/**
* @dev Returns the gas payment token.
*/
function token() public view returns (IERC20) {
return IERC20(_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 {
_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,
@ -51,6 +73,12 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
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 returns (bytes32) {
(address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
@ -58,6 +86,9 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
_token.safeTransferFrom(from, address(this), maxPossibleCharge);
}
/**
* @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 {
(address from, uint256 maxPossibleCharge, uint256 transactionFee, uint256 gasPrice) =
abi.decode(context, (address, uint256, uint256, uint256));

View File

@ -3,6 +3,12 @@ pragma solidity ^0.5.0;
import "./GSNBouncerBase.sol";
import "../../cryptography/ECDSA.sol";
/**
* @dev A xref:ROOT:gsn-advanced.adoc#gsn-bouncers[GSN Bouncer] 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 GSNBouncerSignature is GSNBouncerBase {
using ECDSA for bytes32;
@ -12,10 +18,16 @@ contract GSNBouncerSignature is GSNBouncerBase {
INVALID_SIGNER
}
/**
* @dev Sets the trusted signer that is going to be producing signatures to approve relayed calls.
*/
constructor(address trustedSigner) public {
_trustedSigner = trustedSigner;
}
/**
* @dev Ensures that only transactions with a trusted signature can be relayed through the GSN.
*/
function acceptRelayedCall(
address relay,
address from,

View File

@ -51,6 +51,9 @@ library SafeMath {
*
* Requirements:
* - Subtraction cannot overflow.
*
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
@ -107,6 +110,9 @@ library SafeMath {
*
* Requirements:
* - The divisor cannot be zero.
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
@ -142,6 +148,9 @@ library SafeMath {
*
* Requirements:
* - The divisor cannot be zero.
*
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);

View File

@ -7,7 +7,7 @@ import "../GSN/IRelayRecipient.sol";
// By inheriting from GSNContext, Context's internal functions are overridden automatically
contract GSNContextMock is ContextMock, GSNContext, IRelayRecipient {
function getHubAddr() public view returns (address) {
return _getRelayHub();
return _relayHub;
}
function acceptRelayedCall(
@ -37,7 +37,7 @@ contract GSNContextMock is ContextMock, GSNContext, IRelayRecipient {
}
function getRelayHub() public view returns (address) {
return _getRelayHub();
return _relayHub;
}
function upgradeRelayHub(address newRelayHub) public {

View File

@ -1,50 +0,0 @@
pragma solidity ^0.5.0;
import "../drafts/SignatureBouncer.sol";
import "./SignerRoleMock.sol";
contract SignatureBouncerMock is SignatureBouncer, SignerRoleMock {
function checkValidSignature(address account, bytes memory signature)
public view returns (bool)
{
return _isValidSignature(account, signature);
}
function onlyWithValidSignature(bytes memory signature)
public onlyValidSignature(signature) view
{
// solhint-disable-previous-line no-empty-blocks
}
function checkValidSignatureAndMethod(address account, bytes memory signature)
public view returns (bool)
{
return _isValidSignatureAndMethod(account, signature);
}
function onlyWithValidSignatureAndMethod(bytes memory signature)
public onlyValidSignatureAndMethod(signature) view
{
// solhint-disable-previous-line no-empty-blocks
}
function checkValidSignatureAndData(address account, bytes memory, uint, bytes memory signature)
public view returns (bool)
{
return _isValidSignatureAndData(account, signature);
}
function onlyWithValidSignatureAndData(uint, bytes memory signature)
public onlyValidSignatureAndData(signature) view
{
// solhint-disable-previous-line no-empty-blocks
}
function theWrongMethod(bytes memory) public pure {
// solhint-disable-previous-line no-empty-blocks
}
function tooShortMsgData() public onlyValidSignatureAndData("") view {
// solhint-disable-previous-line no-empty-blocks
}
}

View File

@ -33,6 +33,9 @@ library Address {
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* NOTE: This is a feature of the next version of OpenZeppelin Contracts.
* @dev Get it via `npm install @openzeppelin/contracts@next`.
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));