I have a slightly modified ERC20 contract and I only want to skip charging users ETH for gas for ONLY transfer & transferFrom.
All other transactions/should act as normal and should charge the caller the requisite ETH.
I have a few questions:
Does extending GSNRecipient means that all function calls will automatically attempt ‘relaying’?
Is it possible to get the function that is being relayed from the encodedFunction argument passed into the acceptRelayedCall callback? If yes, how do I get the function name? (This way I can check if it’s a transferFrom or transfer call and make a decision based on that)
Even if 2. is possible, I still don’t know how to make sure all other calls are treated normally (i.e not relayed)
Maybe I’m getting it wrong and the dApp doesn’t attempt to call my contract at all. I think I read something about the call to the relayer being something that happens offchain. Is this the case?
Welcome to the community forum . Thanks for posting your question here.
I was curious why you were only covering transfer and transferFrom. I would have thought that end users would be using transfer and either approve or increaseAllowance with users calling other contracts which would then would call transferFrom.
Does extending GSNRecipient means that all function calls will automatically attempt ‘relaying’?
If your token inherits from GSNRecipient then all functions can be called via the GSN. Your contract should then decide which calls you are willing to pay for.
I also suggest having a look at GSN Bouncers documentation on ways to decide which relayed calls to accept.
GSNBouncerSignature appears to be a potential option for you.
Is it possible to get the function that is being relayed from the encodedFunction argument passed into the acceptRelayedCall callback? If yes, how do I get the function name? (This way I can check if it’s a transferFrom or transfer call and make a decision based on that)
You can get the function name in acceptRelayedCall:
acceptRelayedCall encodedFunction is the relayed call calldata, so its first four bytes are the function selector.
If you use GSNBouncerSignature you can decide whether to sign or not sign the relayed call parameters (including based on the encodedFunction.
Even if 2. is possible, I still don’t know how to make sure all other calls are treated normally (i.e not relayed)
Your contract (or off-chain signing) will need to use your logic to decide which relayed calls to accept. So you can reject relayed calls for any function that you don't want to pay for.
Maybe I’m getting it wrong and the dApp doesn’t attempt to call my contract at all. I think I read something about the call to the relayer being something that happens offchain. Is this the case?
Your dapp can call a GSN relayer (for functions that it wants to use the GSN for) which will then relay the calls (that your contract accepts based on its business logic).
Your dapp call call a function using injected Web3 (e.g. MetaMask) for functions that you don't want to pay for.
If you haven't already, I recommend playing with the GSN Starter Kit
This will depend on the strategy you choose to accept or reject relayed calls. If you do off-chain signing with GSNBouncerSignature, like @abcoathup suggested, you could filter via selector on your signing server. You could also filter on-chain by adding this logic to acceptRelayedCall, something like this (pseudo-code):
function acceptRelayedCall(address, address, bytes calldata encodedFunction, uint256, uint256, uint256, uint256, bytes calldata approvalData, uint256) external view returns (uint256, bytes memory) {
if (getSelector(encodedFunction) != approvedSelector) { // getSelector should extract the first 4 bytes
return _rejectRelayedCall(uint256(GSNBouncerSignatureErrorCodes.INVALID_SIGNER));
} else {
return super.acceptRelayedCall(<params>);
}
}