Hello I’m trying to understand if I need to implement GSN contracts on all my contracts that are involved in the transaction. My current abbreviated implementation is outlined below.
contract Foo is Initializable, GSNRecipientSignature {
MyToken private _token;
address _signer;
function initialize(address signer) {
GSNRecipientSignature.initialize(signer);
_signer = signer;
}
// This function gets called by our dapp
function doSomething(to, amount) {
_token.transfer(to, amount)
}
function acceptRelayedCall(...omitted) {
// accept or reject
}
}
contract MyToken is ERC20 {}
So based on my implementation, does MyToken have to implement a GSN contract also?
I do have a second question. Calling the ERC20 transfer function uses _msgSender() under the hood, is _msgSender() the contract Foo executing the transaction in the context of the doSomething() method?
Contracts being directly called by the GSN (Relayer) need to be GSN enabled.
In the example Foo, doSomething transfer's tokens held by the Foo contract to an address. So only Foo would need to be GSN enabled.
For the situation where an ERC20 token holder interacts with another contract using the token, two transactions are required:
The token holder calls approve to set an allowance of tokens that the contract can use. (assuming an OpenZeppelin ERC20 implementation can use increaseAllowance)
The token holder calls the contract to perform an action and the contract can transferFrom an amount of tokens within the set allowance.
For both these transactions to occur via the GSN then both contracts need to be GSN enabled.
In the example _msgSender() in MyToken would be Foo when doSomething() was called. Which means that Foo would need to be holding an amount of MyToken.
@abcoathup After looking at the ERC20 code now, I didn’t notice that increaseAllowance calls _msgSender() and now I know that the contract is the sender in the context of the caller it makes sense to me now that the token would have to implement GSN. Thanks again!
I created a custom function on my token so that only my smart contract is allowed to approve itself
MyContract private _myContract; // using an onlyOwner method to set the myContract
function setMyContract(MyContract myContract) public onlyOwner {
_myContract = myContract;
}
function contractApproval(address user, uint256 amount) public returns(bool) {
// there is another contract I have where I whitelist my dapp users, but outside of the scope of this conversation
require(_accessControl.isUserWhitelisted(user), "The user must be whitelisted.");
// Using 0x contract-utils cause they are awesome!
require(LibAddress.isContract(_msgSender()), "Recipient must be a contract.");
require(_msgSender() == address(_myContract), "Only my contract is allowed to execute contractApproval.");
// using the internal function _approve in the ERC20 contract so that I can set the contract as the spender
_approve(user, _msgSender(), amount);
return true;
}
I could potentially whitelist my contract address, which might be a good idea to