Enabling an ERC20 and a contract receiving the ERC20 token for Gas Station Network

Hi @abcoathup,

By “unless the token was also GSN enabled” you mean that if the ERC20 is GSN enabled a user won’t need to approve a contract to spend its tokens on his behalf ? How will the approval work in this case ?

Thanks

1 Like

Hi @Moun,

Welcome to the community forum :wave:. Thanks for posting your question here.

To use an ERC20 token with a contract receiving tokens requires two transactions:

  1. Approve: The user calls approve to set an allowance for the ERC20 token to use with the contract receiving tokens. (or use increaseAllowance)
  2. Transfer From: The user calls the contract which then calls the ERC20 token transferFrom function to transfer an amount of ERC20 token to the contract receiving tokens.

If only the contract is GSN enabled, then users would still need to have Ether to call approve to set an allowance.

Otherwise for users not to require Ether, both the ERC20 token and the contract receiving tokens would both need to be GSN enabled. This would still require two transactions.


Alternatively to ERC20 you could look at creating ERC777 tokens (no need to do approve and transferFrom in two separate transactions). See the documentation for details: https://docs.openzeppelin.com/contracts/2.x/tokens#ERC777

To use the GSN, the ERC777 token would need to be GSN enabled.


To make a contract GSN enabled the contract would need to inherit from GSNRecipient and include functionality to decide which relayed calls to accept. I suggest reading the documentation on Writing GSN-capable contracts:
https://docs.openzeppelin.com/contracts/2.x/gsn

When looking at enabling a contract for the Gas Station Network, I recommend also looking at the documentation for GSN Bouncers on how to approve GSN relay calls.

@Dennison has created a tutorial on using GSNBouncerSignature: Advanced GSN: GSNBouncerSignature.sol

1 Like

Thanks Andrew for your reply. I’ll have a look at ERC777.

For ERC20, I’m trying to understand how a user can approve a third party contract using this ERC20 implementation. I looked at the code and can’t figure out the mechanism https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol, I only see _msgSender() that’s been added.

Can you please explain this part ?

Thanks

1 Like

Hi @Moun,

An example ERC20 token that is GSN enabled is shown below (Simpletoken). Please note, this token is configured to accept all relayed calls (which is not something you would want to do in production).

To enable a contract for the GSN, the contract needs to inherit from GSNRecipient and have a mechanism for deciding which relayed calls to accept.
See the documentation on Writing GSN-capable contracts for more details.

In the SimpleToken example, the contract inherits from GSNRecipient and implements the function acceptRelayedCall which accepts all relayed calls…

Rather than accepting all relayed calls in the SimpleToken example, a GSN Bouncer should be used, such as GSNSignatureBouncer (where off-chain logic decides whether or not the relayed call should be accepted). See the documentation on GSN Bouncers for more details.

SimpleToken.sol

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts/GSN/GSNRecipient.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is ERC20, ERC20Detailed, GSNRecipient {

    /**
     * @dev Constructor that gives _msgSender() all of existing tokens.
     */
    constructor () public ERC20Detailed("SimpleToken", "SIM", 18) {
        _mint(_msgSender(), 10000 * (10 ** uint256(decimals())));
    }

    // accept all requests
    function acceptRelayedCall(
        address,
        address,
        bytes calldata,
        uint256,
        uint256,
        uint256,
        uint256,
        bytes calldata,
        uint256
    ) external view returns (uint256, bytes memory) {
        return _approveRelayedCall();
    }
}
1 Like

Hey @abcoathup,

Thanks for the details. I got what you mean now, there is some logic to be added to the ERC20…

Do you think that this could introduce some centralisation to the ERC20 itself ? In that sens, it seems better to separate the concerns by having another contract make gasless transactions. Am I missing something here ?

Cheers

1 Like

Hi @Moun,

Interested to hear how you see this as introducing centralization?

The only centralization I see is that you have logic which decides which relayed calls will be accepted (i.e. which transactions will be paid for) and the business logic for this could be off chain, but you are paying for transactions, so you need this logic.

Users still have the option to pay for their own transactions using Ether, regardless of whether you offer to accept relayed calls via the Gas Station Network.

If the ERC20 token isn’t GSN enabled, then Ether will be required by token holders to either approve an allowance or transfer the token to a contract that is GSN enabled.

1 Like

Hi @Moun,

I wanted to check how you were getting on?

Hi @abcoathup,

Thanks for your reply, that’s exactly the point of “centralisation” I was thinking about. I agree, users have a choice either way.

Have a nice day!

1 Like