Merge branch 'master' into release-v2.4.0
This commit is contained in:
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
|
||||
Reference in New Issue
Block a user