diff --git a/contracts/GSN/GSNRecipient.sol b/contracts/GSN/GSNRecipient.sol index 5b6f5932d..26ab35083 100644 --- a/contracts/GSN/GSNRecipient.sol +++ b/contracts/GSN/GSNRecipient.sol @@ -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 _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,6 +30,11 @@ 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(_relayHub).withdraw(amount, payee); } diff --git a/contracts/GSN/README.adoc b/contracts/GSN/README.adoc index 009c0fdd2..b2758b949 100644 --- a/contracts/GSN/README.adoc +++ b/contracts/GSN/README.adoc @@ -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 diff --git a/contracts/GSN/bouncers/GSNBouncerERC20Fee.sol b/contracts/GSN/bouncers/GSNBouncerERC20Fee.sol index a0acec854..817cb0e36 100644 --- a/contracts/GSN/bouncers/GSNBouncerERC20Fee.sol +++ b/contracts/GSN/bouncers/GSNBouncerERC20Fee.sol @@ -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)); diff --git a/contracts/GSN/bouncers/GSNBouncerSignature.sol b/contracts/GSN/bouncers/GSNBouncerSignature.sol index fca944681..3432de433 100644 --- a/contracts/GSN/bouncers/GSNBouncerSignature.sol +++ b/contracts/GSN/bouncers/GSNBouncerSignature.sol @@ -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, diff --git a/docs/modules/ROOT/pages/gsn-advanced.adoc b/docs/modules/ROOT/pages/gsn-advanced.adoc index 2766acd45..bea1df71a 100644 --- a/docs/modules/ROOT/pages/gsn-advanced.adoc +++ b/docs/modules/ROOT/pages/gsn-advanced.adoc @@ -8,6 +8,7 @@ Finally, we will cover how to create your own custom Bouncer. If you're still learning about the basics of the Gas Station Network, you should head over to our xref:api:gsn.adoc[GSN Guide], which will help you get started from scratch. +[[gsn-bouncers]] == GSN Bouncers A *GSN Bouncer* decides which transaction gets approved and which transaction gets rejected. Bouncers are a key concept within GSN. Dapps need Bouncers to prevent malicious users from spending the subsidies for the transactions. diff --git a/docs/modules/ROOT/pages/gsn.adoc b/docs/modules/ROOT/pages/gsn.adoc index 0b8b6d7f4..370eaac99 100644 --- a/docs/modules/ROOT/pages/gsn.adoc +++ b/docs/modules/ROOT/pages/gsn.adoc @@ -87,4 +87,4 @@ These functions allow you to implement, for instance, a flow where you charge yo == Further reading -Read our xref:gsn-advanced.adoc[guide on the payment strategies] (called _bouncers_) pre-built and shipped in OpenZeppelin Contracts, or check out xref:api:gsn.adoc[the API reference of the GSN base contracts]. +Read our xref:gsn-advanced.adoc[guide on the payment strategies] (called _bouncers_) pre-built and shipped in OpenZeppelin Contracts, or check out xref:api:GSN.adoc[the API reference of the GSN base contracts].