Are ERC20 tokens already GSN capable?

Hi all :slightly_smiling_face:
I’m playing around using OpenZeppelin GasStationNetwork, I followed https://docs.openzeppelin.com/sdk/2.6/gsn-dapp guide to understand the basics.
Now I’m trying to add GSN capabilities to an ERC721 token but I’m failing at compile time because the compiler warns me with this error

✖ Compiling contracts with solc 0.5.13 (commit.5b0b510c)
Compilation errors:
contracts/Color.sol:5:1: DeclarationError: Identifier already declared.
import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";
^-------------------------------------------------------------------------------^
@openzeppelin/contracts/GSN/Context.sol:13:1: The previous declaration is here:
contract Context {
^ (Relevant source part starts here and spans across multiple lines).

I saw that GSN/context is already imported in ERC721.sol contract, so I’m asking if it means that ERC721.sol is already GSN capable without importing a GSNRecipient in my NotFungibleToken implementation or not?
What I’m getting wrong?

Any help would be apreciated :smiley:

:computer: Environment
OpenZeppelin 2.6.0
OpenZeppelin Contracts 2.4.0
WSL Ubuntu

:1234: Code to reproduce

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721Mintable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";


contract Color is ERC721Full, ERC721Mintable {
string[] public colors;
mapping(string => bool) _colorExists;

// ERC721Full("Color", "COLOR") is used to call contract's constructor we inherit from
constructor() ERC721Full("Color", "COLOR") public {
}

/*function mint(string memory _color) public {
    // Require unique color
    require(!(_colorExists[_color]), 'This color already exists');
    uint _id = colors.push(_color);
    _mint(msg.sender, _id);
    // Add color & track it
    _colorExists[_color] = true;
}*/
}
1 Like

Hi @LucaAsga,

Welcome to the community :wave:

For a contract to be a GSN recipient it needs three things, whilst OpenZeppelin Contracts (and OpenZeppelin Contracts Ethereum Package) handle msg.sender and msg.data differently, contracts we write need to do all three.

https://docs.openzeppelin.com/contracts/2.x/gsn-strategies
A GSN recipient contract needs the following to work:

  1. It needs to have funds deposited on its RelayHub.
  2. It needs to handle msg.sender and msg.data differently.
  3. It needs to decide how to approve and reject relayed calls.

The reason for the compilation error is that for upgradeable contracts we need to use @openzeppelin/contracts-ethereum-package.

https://docs.openzeppelin.com/sdk/2.5/linking
NOTE: Make sure you install @openzeppelin/contracts-ethereum-package and not the vanilla @openzeppelin/contracts . The latter is set up for general usage, while @openzeppelin/contracts-ethereum-package is tailored for being used with the OpenZeppelin SDK. This means that its contracts are already set up to be upgradeable.

I recently created an issue to add a warning to users: OpenZeppelin/openzeppelin-sdk#1297 .
There is also a plan to remove this requirement to use the Contracts Ethereum Package version: Planning the demise of OpenZeppelin Contracts' evil twin.

Also with upgradeable contracts we need to use initializers rather than constructors.

https://docs.openzeppelin.com/sdk/2.5/writing-contracts
You can use your Solidity contracts in the OpenZeppelin SDK without any modifications, except for their constructors. Due to a requirement of the proxy-based upgradeability system, no constructors can be used in upgradeable contracts.

Simple721Token (GSN enabled)

The following ERC721 is GSN enabled and accepts all relayed calls.
For a production dapp we would want to decide which calls to accept so should implement a GSN Strategy (see https://docs.openzeppelin.com/contracts/2.x/gsn-strategies)

pragma solidity ^0.5.5;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/ERC721Full.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/ERC721Mintable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";

contract Simple721Token is Initializable, ERC721Full, ERC721Mintable, GSNRecipient {

    function initialize(address sender) public initializer {
        ERC721.initialize();
        ERC721Metadata.initialize("Token", "TKN");
        ERC721Enumerable.initialize();
        ERC721Mintable.initialize(sender);
    }

    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    ) external view returns (uint256, bytes memory) {
        return _approveRelayedCall();
    }

    function _preRelayedCall(bytes memory context) internal returns (bytes32) {
    }

    function _postRelayedCall(bytes memory context, bool, uint256 actualCharge, bytes32) internal {
    }
}

Please ask all the questions that you need.

Hi @LucaAsga,

Checking that the above answered your question.

If so, you can mark it as the solution. If not, please ask all the questions that you need.