*`Address.isContract`: switched from `extcodesize` to `extcodehash` for less gas usage. ([#1802](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1802))
*`Address.isContract`: switched from `extcodesize` to `extcodehash` for less gas usage. ([#1802](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1802))
*`SafeMath`: added custom error messages support for `sub`, `div` and `mod` functions. `ERC20` and `ERC777` updated to throw custom errors on subtraction overflows. ([#1828](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1828))
*`SafeMath`: added custom error messages support for `sub`, `div` and `mod` functions. `ERC20` and `ERC777` updated to throw custom errors on subtraction overflows. ([#1828](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1828))
### Bugfixes
### Breaking changes in drafts:
*`SignatureBouncer` has been removed from the library, both to avoid confusions with the GSN Bouncers and `GSNBouncerSignature` and because the API was not very clear. ([#1879](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1879))
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].
* @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 {
contract GSNBouncerERC20Fee is GSNBouncerBase {
using SafeERC20 for __unstable__ERC20PrimaryAdmin;
using SafeERC20 for __unstable__ERC20PrimaryAdmin;
using SafeMath for uint256;
using SafeMath for uint256;
...
@@ -17,18 +26,31 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
...
@@ -17,18 +26,31 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
__unstable__ERC20PrimaryAdmin private _token;
__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 {
constructor(string memory name, string memory symbol, uint8 decimals) public {
_token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
_token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
}
}
/**
* @dev Returns the gas payment token.
*/
function token() public view returns (IERC20) {
function token() public view returns (IERC20) {
return IERC20(_token);
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 {
function _mint(address account, uint256 amount) internal {
_token.mint(account, amount);
_token.mint(account, amount);
}
}
/**
* @dev Ensures that only users with enough gas payment token balance can have transactions relayed through the GSN.
*/
function acceptRelayedCall(
function acceptRelayedCall(
address,
address,
address from,
address from,
...
@@ -51,6 +73,12 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
...
@@ -51,6 +73,12 @@ contract GSNBouncerERC20Fee is GSNBouncerBase {
This guide shows you different strategies (Bouncers) to accept transactions via Gas Station Network.
This guide shows you different strategies (Bouncers) to accept transactions via Gas Station Network.
First, we will explain the Bouncer concept and then we will showcase how to use the two most common strategies.
First, we will explain the Bouncer concept, and then we will showcase how to use the two most common strategies.
Finally, we will cover how to create your own custom Bouncer.
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.
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
== 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 to spend the transactions subsidy.
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.
As we have seen in the Basic Guide, in order to use GSN, your contracts need to extend from GSN Recipient.
As we have seen in the Basic Guide, in order to use GSN, your contracts need to extend from GSN Recipient.
...
@@ -24,7 +25,7 @@ The first can be done via the https://gsn.openzeppelin.com/recipients[GSN Tools]
...
@@ -24,7 +25,7 @@ The first can be done via the https://gsn.openzeppelin.com/recipients[GSN Tools]
The sender and data can be used safely when using `GSNRecipient` and _msgSender & _msgData.
The sender and data can be used safely when using `GSNRecipient` and _msgSender & _msgData.
The third is a bit more complex. The GSN Recipient, by default, will accept and pay for all transactions. 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 contracts _GSNBouncers_.
The third is a bit more complex. The GSN Recipient, by default, will accept and pay for all transactions. 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 contracts _GSNBouncers_.
We include two of them below, ready to use out of the box.
We include two of them below, ready to use out of the box.
...
@@ -34,20 +35,20 @@ This bouncer lets users call into your recipient contract via the GSN (charging
...
@@ -34,20 +35,20 @@ This bouncer lets users call into your recipient contract via the GSN (charging
The signature used to create the transaction must be added to the contract as a trusted signer. If it is not the same, this bouncer will not accept the transaction.
The signature used to create the transaction must be added to the contract as a trusted signer. If it is not the same, this bouncer will not accept the transaction.
This means that you need to set up a system where your trusted account signs call requests, if they are valid users.
This means that you need to set up a system where your trusted account signs call requests, as long as they are valid users.
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 oauth and validation e.g. gone through a captcha, or validated their email address.
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 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 transactions (e.g. 5 requests via the GSN, at which point they need to create a wallet).
You could restrict it further and let new users send a specific number of transactions (e.g., 5 requests via the GSN, at which point they need to create a wallet).
Alternatively, you could charge them off-chain (e.g. via credit card) for credit on your system, and let them run GSN calls until said credit runs out.
Alternatively, you could charge them off-chain (e.g., via credit card) for credit on your system and let them run GSN calls until said credit runs out.
The great thing about this setup is that *your contract doesn't need to change*. All you're doing is changing the backend logic conditions under which users call into your contract for free.
The great thing about this setup is that *your contract doesn't need to change*. All you're doing is changing the backend logic conditions under which users call into your contract for free.
On the other hand, you need to have a backend server, microservice or lambda function to accomplish this.
On the other hand, you need to have a backend server, microservice, or lambda function to accomplish this.
=== How does it work?
=== How does it work?
Here is the definition of acceptRelayedCall function.
Here is the definition of acceptRelayedCall function.
It decides whether or not accept the call based on the signature. No further gsn-actions need to be taken.
It decides whether or not to accept the call based on the signature. No further gsn-actions need to be taken.
It only relies on the approvalData and does not use callData.
It only relies on the approvalData and does not use callData.
...
@@ -118,7 +119,7 @@ On the other hand, when the signatures don't match, the call gets rejected with
...
@@ -118,7 +119,7 @@ On the other hand, when the signatures don't match, the call gets rejected with
----
----
=== How to use it?
=== How to use it
Create your contract using the following:
Create your contract using the following:
...
@@ -131,14 +132,14 @@ Create your contract using the following:
...
@@ -131,14 +132,14 @@ Create your contract using the following:
== GSNBouncerERC20Fee
== GSNBouncerERC20Fee
This bouncer is a bit more complex (but don't worry we've already wrote it for you!). Unlike `GSNBouncerSignature`, this Bouncer doesn't require any off-chain services.
This bouncer is a bit more complex (but don't worry, we've already written it for you!). Unlike `GSNBouncerSignature`, this Bouncer doesn't require any off-chain services.
Instead of manually approving each transaction, you will give tokens to your users. These tokens are then used to pay for GSN calls to your recipient contract.
Instead of manually approving each transaction, you will give tokens to your users. These tokens are then used to pay for GSN calls to your recipient contract.
Any user that has enough tokens is automatically approved and the recipient contract will cover his transaction costs!
Any user that has enough tokens is automatically approved and the recipient contract will cover his transaction costs!
This bouncer charges users for the ether cost your recipient will incur. Each recipient contract has their own unique token, with a baked-in exchange rate of 1:1 to ether, since they act as an ether replacement when using the GSN.
This bouncer charges users for the ether cost your recipient will incur. Each recipient contract has their own unique token, with a baked-in exchange rate of 1:1 to ether, since they act as an ether replacement when using the GSN.
The recipient has an internal mint function. Firstly, you need to setup a way to call it (e.g. add a public function with onlyOwner or some other form of access control).
The recipient has an internal mint function. Firstly, you need to setup a way to call it (e.g., add a public function with onlyOwner or some other form of access control).
Then issue tokens to users based on your business logic. For example, you could mint limited tokens to new users, mint tokens when they buy them off-chain, give tokens based on the user subscription etc.
Then, issue tokens to users based on your business logic. For example, you could mint limited tokens to new users, mint tokens when they buy them off-chain, give tokens based on the user subscription, etc.
NOTE: *Users do not need call approve* on their tokens for your recipient to use them. They are a modified ERC20 variant that lets the recipient contract retrieve them.
NOTE: *Users do not need call approve* on their tokens for your recipient to use them. They are a modified ERC20 variant that lets the recipient contract retrieve them.
...
@@ -173,10 +174,10 @@ function acceptRelayedCall(
...
@@ -173,10 +174,10 @@ function acceptRelayedCall(
}
}
----
----
The bouncer rejects the tx if the real sender doesn't have enough tokens or it is not allowed to spend that amount.
The bouncer rejects the tx if the real sender doesn't have enough tokens or they are not allowed to spend that amount.
If the sender can spend the tokens, the bouncers approves the transaction and overrides _confirmRelayedCall to make that data available to pre and post.
If the sender can spend the tokens, the bouncers approve the transaction and overrides _confirmRelayedCall to make that data available to pre and post.
Let's see now how we perform the token transfer inside the _preRelayedCall method.
Now, let's see how we perform the token transfer inside the _preRelayedCall method.
This is required to protect the contract from exploits (this is really similar to how ether is locked in Ethereum transactions).
This is required to protect the contract from exploits (this is really similar to how ether is locked in Ethereum transactions).
Please not how the gas cost estimation is not 100% accurate, we may tweak it further down the road.
Please note how the gas cost estimation is not 100% accurate, we may tweak it further down the road.
NOTE: `_preRelayedCall` and `_postRelayedCall` are used instead of preRelayedCall and postRelayedCall. This prevents them from being called by non-relayhub. Always use _pre and _post methods.
NOTE: `_preRelayedCall` and `_postRelayedCall` are used instead of preRelayedCall and postRelayedCall. This prevents them from being called by non-relayhub. Always use _pre and _post methods.
=== How to use it?
=== How to use it
Create your contract using the following:
Create your contract using the following:
...
@@ -233,6 +234,6 @@ Create your contract using the following:
...
@@ -233,6 +234,6 @@ Create your contract using the following:
You can use 'GSNBouncerBase' as an example to guide your Bouncer implementation.
You can use 'GSNBouncerBase' as an example to guide your Bouncer implementation.
The only thing you must do is to extend from `GSNRecipient` and implement the accept method.
The only thing you must do is extend from `GSNRecipient` and implement the accept method.
Depending on your logic, you may need to implement `_postRelayedCall` and `_preRelayedCall`.
Depending on your logic, you may need to implement `_postRelayedCall` and `_preRelayedCall`.
The https://gsn.ethereum.org[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.
User onboarding is one of the hottest topics in Ethereum. UI/UX along with scalability have been identified as the main problems that prevent adoption. Meta-transactions are a key component to improve the user experience.
If you're new to the GSN, you probably want to first take a look at the xref:ROOT:gsn/what-is-the-gsn.adoc[light overview of the system], to get a clearer picture of how gasless transactions are achieved. Otherwise, strap in!
Here you'll learn all about the Gas Station Network (GSN) and how to write contracts that don't require their users to hold Ether to pay for gas.
If you're already up to speed with the workings of the GSN, feel free to skip to <<Receiving a relayed call>>, where we'll go over how to use the OpenZeppelin Contracts to easily write a GSNRecipient contract. Otherwise, strap in!
NOTE: This feature is being released in the next version of `@openzeppelin/contracts`, available right now through `npm install @openzeppelin/contracts@next`. It is also available on the `@openzeppelin/contracts-ethereum-package` variant.
WARNING: This feature is under development, and will be released in the next version of `@openzeppelin/contracts`.
== Sending gas-less transactions
All Ethereum transactions use gas, and the sender of each transaction must have enough Ether to pay for the gas spent. Even though these gas costs are low for basic transactions (a couple of cents), getting this Ether is no easy task: dApp users often need to go through Know Your Customer and Anti Money-Laundering processes (KYC & AML), which not only takes time but often involves sending a selfie holding their passport over the Internet (!).
On top of that, they also need to provide financial information to be able to purchase Ether through an exchange.
Only the most hardcore users will put up with this hassle, and dApp adoption greatly suffers when Ether is required. We can do better.
**Enter meta-transactions**. This is a fancy name for a simple idea: a third-party can send another user's transactions and pay themselves for the gas cost. That's it! There's some tricky technical details, but those can be safely ignored when interacting with the GSN. This way, instead of your users calling into your contract (called the _recipient_) directly, someone else (we'll call them a _relayer_) will send their transaction and pay for the cost.
But why would they do such a thing?
== Incentives
Relayers are not running a charity: they're running a business. The reason why they'll gladly pay for your users' gas costs is because they will in turn charge your contract, the recipient. That way relayers get their money back, plus a bit extra as a _fee_ for their services.
This may sound strange at first, but paying for user onboarding is a very common business practice. Lots of money is spent on advertising, free trials, new user discounts, etc., all with the https://en.wikipedia.org/wiki/Customer_acquisition_cost[goal of user acquisition]. Compared to those, the cost of a couple of Ethereum transactions is actually very small.
Additionally, you can leverage the GSN in scenarios where your users pay you off-chain in advance (e.g. via credit card), with each GSN-call deducting from their balance on your system. The possibilities are endless!
=== Should I trust these relayers?
You don't need to! The GSN is set up in such a way where it's in the relayers' best interest to serve your requests, and there are measures in place to penalize them if they misbehave. All of this happens automatically, so you can safely start using their services worry-free.
== One contract to coordinate them all
There are many meta-transaction implementations out there, but the GSN has a unique detail that makes it special. Inside its core, a smart contract is responsible for keeping track of relayers, handling relayed transactions, charging their recipients, and generally ensuring all parties stay honest. This contract is called RelayHub, and there is a _single_ instance of it in the whole network (you don't need to deploy your own!). Think of it as a piece of public infrastructure, for all Ethereum users to benefit from.
One of RelayHub's jobs is to act as a, well, _hub_ for all relayers: they will advertise their services on this contract, and your users will query it to find the relayer that best suits their purposes. This is out of scope for this guide however, and is not something you need to worry about when writing a recipient contract. If you want to learn more about sending transactions via relayers, head to our https://github.com/OpenZeppelin/openzeppelin-gsn-provider[GSNProvider guide].
The other key task RelayHub carries out is the actual _relaying of transactions_, the sole purpose behind this whole system. Instead of calling a function in your contract directly, your users will request a relayer to do it for them, who will then execute RelayHub's relayCall function. RelayHub will verify that the transaction is legitimate (protecting both users and recipients from dishonest relayers), and then call into your contract as originally requested by your user. This requires your recipient trusting RelayHub to do the right thing, but since it is a smart contract, this is as simple as reading its source code!
NOTE: The RelayHub address will be the same in each network. Right now, the latest relay hub is live with this address `0xD216153c06E857cD7f72665E0aF1d7D82172F494`.
== Receiving a relayed call
== Receiving a relayed call
We've mentioned how the RelayHub, and not your user, the one that actually ends up calling a function in your contract. We will refer to this as the _relayed call_. OpenZeppelin Contracts includes a number of utilities to make receiving relayed calls as easy as developing a regular Solidity contract, without needing to worry about the low level details.
The first step to writing a recipient is to inherit from our GSNRecipient contract. If you're also inheriting from other OpenZeppelin contracts, such as ERC20 or Crowdsale, this will work just fine: adding GSNRecipient to all of your token or crowdsale functions will make them GSN-callable.
The first step to writing a recipient is to inherit from our GSNRecipient contract. If you're also inheriting from other OpenZeppelin contracts, such as ERC20 or Crowdsale, this will work just fine: adding GSNRecipient to all of your token or crowdsale functions will make them GSN-callable.
```solidity
```solidity
...
@@ -124,17 +87,6 @@ function postRelayedCall(
...
@@ -124,17 +87,6 @@ function postRelayedCall(
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.
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.
== Payment
By now you may be wondering how exactly relayers charge their recipients for gas costs and service fees. The answer is simple: each recipient must have funds deposited on RelayHub in advance, and payment is automatically handled on each relayed call.
You can head to the https://gsn.openzeppelin.com/recipients[GSN Recipient Tool] to check and top-up your contracts' balance, view previous charges, or do all of this programatically by calling `IRelayHub.depositFor` and `IRelayHub.balanceOf`.
Recipients may withdraw their balance from the system at any point, but remember that they will not be able to receive any further relayed calls!
== Further reading
== Further reading
* The https://medium.com/@rrecuero/eth-onboarding-solution-90607fb81380[GSN announcement post] provides a good *overview of the system*, along with some use cases to take inspiration from.
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].
* If you want to learn how to use *OpenZeppelin Contract's pre-made accept and charge strategies*, head to the xref:gsn_advanced.adoc[Advanced GSN Guide].
* If instead you wish to know more about how to *use GSN from your application*, head to the https://github.com/OpenZeppelin/openzeppelin-gsn-provider[OpenZeppelin GSN provider guides].
* For information on how to *test GSN-enabled contracts*, go to the https://github.com/OpenZeppelin/openzeppelin-gsn-helpers[OpenZeppelin test helpers documentation].