-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Features: Gasless transactions. #89
Changes from all commits
5fa4a06
aae803d
1596742
a1fa47f
18f7175
e20eb2b
7fdbf82
2beca99
5fe5563
2085694
94a2233
9727713
b362ca4
a372140
fefbd7a
ef7f5b2
8eb88fc
dc82112
10c20f2
7eb6a26
683c150
0bbcac9
2e2fd46
5a472c5
bdaf271
89f84c3
0ce0684
74e6cfa
66ec228
a0a22de
588a177
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.10; | ||
|
||
/** | ||
* @title The Context behaviour abstract contract. | ||
* @notice The AHasContext abstract contract is in charge of adding the required | ||
* base implementation of ERC-2771 - see: https://eips.ethereum.org/EIPS/eip-2771 | ||
* the key difference is that `isTrustedForwarder` becomes an internal method. | ||
*/ | ||
abstract contract AHasContext { | ||
/// @dev Must be implemented by the inheriting contract. | ||
function _isTrustedForwarder(address forwarder) internal view virtual returns (bool); | ||
|
||
// The following is copied from: | ||
// https://github.com/opengsn/gsn/blob/v3.0.0-beta.10/packages/contracts/src/ERC2771Recipient.sol | ||
// With a slight change to call `_isTrustedForwarder` instead of `isTrustedForwarder`. | ||
|
||
/// @notice Default implemention. | ||
function _msgSender() internal view virtual returns (address ret) { | ||
if (msg.data.length >= 20 && _isTrustedForwarder(msg.sender)) { | ||
// At this point we know that the sender is a trusted forwarder, | ||
// so we trust that the last bytes of msg.data are the verified sender address. | ||
// extract sender address from the end of msg.data | ||
assembly { | ||
ret := shr(96, calldataload(sub(calldatasize(), 20))) | ||
} | ||
} else { | ||
ret = msg.sender; | ||
} | ||
return ret; | ||
} | ||
|
||
/// @notice Default implemention. | ||
function _msgData() internal view virtual returns (bytes calldata ret) { | ||
if (msg.data.length >= 20 && _isTrustedForwarder(msg.sender)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see this condition often. Maybe having it in a private function such as This could then be rewritten such as: return isValidTrustedForwarder() ? msg.data[...] : msg.data; |
||
return msg.data[0:msg.data.length - 20]; | ||
} else { | ||
return msg.data; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.10; | ||
|
||
import "./lib/LibHasForwarder.sol"; | ||
import "../interfaces/ICustomErrors.sol"; | ||
import "../interfaces/IERC165.sol"; // Interface Support. | ||
|
||
import "@opengsn/contracts/src/forwarder/IForwarder.sol"; | ||
|
||
/** | ||
* @title The Forwarder behaviour abstract contract. | ||
* @notice The AHasForwarder abstract contract is in charge of adding the Forwarder | ||
* functionality to any contract inheriting from it. | ||
*/ | ||
abstract contract AHasForwarder { | ||
/// Errors. | ||
|
||
/** | ||
* @notice Happens when a function is called by a non forwarder manager. | ||
* @param who is the address that called the function. | ||
*/ | ||
error RequiresForwarderManager(address who); | ||
|
||
/// Events. | ||
|
||
/** | ||
* @notice Emited when a forwarder is set on an implementing contract. | ||
* @param forwarderAddress is the address of the trusted forwarder. | ||
*/ | ||
event ForwarderChanged(address forwarderAddress); | ||
|
||
// SEE: https://github.com/opengsn/gsn/blob/v3.0.0-beta.10/packages/contracts/src/ERC2771Recipient.sol | ||
|
||
/** | ||
* @notice ERC2771Recipient implementation. | ||
* @param _forwarderAddress the forwarder address. | ||
* @return bool if the forwarder is trusted. | ||
*/ | ||
function isTrustedForwarder(address _forwarderAddress) public view returns (bool) { | ||
return _forwarderAddress == LibHasForwarder.data().forwarderAddress; | ||
} | ||
|
||
/** | ||
* @notice ERC2771Recipient implementation. | ||
* @param _forwarderAddress the forwarder address. | ||
*/ | ||
function setTrustedForwarder(address _forwarderAddress) external onlyForwarderManager { | ||
if (!IERC165(_forwarderAddress).supportsInterface(type(IForwarder).interfaceId)) | ||
revert ICustomErrors.InterfaceNotSupported("IForwarder"); | ||
|
||
LibHasForwarder.Data storage ds = LibHasForwarder.data(); | ||
ds.forwarderAddress = _forwarderAddress; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there any tests we want to do before setting a new forwarder? For example check that it implements a particular interface? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, would be good to handle that 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
emit ForwarderChanged(_forwarderAddress); | ||
} | ||
|
||
/** | ||
* WARNING: The Forwarder can have a full control over your Recipient. Only trust verified Forwarder. | ||
* @notice Method is not a required method to allow Recipients to trust multiple Forwarders. Not recommended yet. | ||
* @return forwarderAddress The address of the Forwarder contract that is being used. | ||
*/ | ||
function getTrustedForwarder() public view virtual returns (address forwarderAddress) { | ||
return LibHasForwarder.data().forwarderAddress; | ||
} | ||
|
||
/** | ||
* @notice Checks whether the given address is a forwarder manager or not. | ||
* @dev Must be implemented by the inheriting contract. | ||
* @param who is the address to test. | ||
*/ | ||
function isValidForwarderManager(address who) internal view virtual returns (bool); | ||
|
||
/// Modifiers. | ||
|
||
/// @notice Ensures that a method can only be called by the forwarder manager. | ||
modifier onlyForwarderManager() virtual { | ||
if (!isValidForwarderManager(msg.sender)) revert RequiresForwarderManager(msg.sender); | ||
_; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.10; | ||
|
||
library LibHasForwarder { | ||
// The current version of the storage. | ||
uint16 internal constant STORAGE_VERSION = 1; | ||
// This is `keccak256('HasForwarder.storage.Main')`. | ||
bytes32 internal constant STORAGE_SLOT = 0xa9930c2ffa1b605b0243ba36b3020146bcba5a29c05a711f5ca7c705a8e851ca; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. |
||
|
||
struct Data { | ||
/// @notice The latest intializer version that was called. | ||
uint16 version; | ||
/// @notice This is where we store the trusted forwarder address. | ||
address forwarderAddress; | ||
} | ||
|
||
function data() internal pure returns (Data storage s) { | ||
assembly { | ||
s.slot := STORAGE_SLOT | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.10; | ||
|
||
import "../common/AHasForwarder.sol"; | ||
import "./lib/AFastFacet.sol"; | ||
|
||
/** | ||
* @title The Fast forwardable contract. | ||
* @notice The Fast Forwardable facet is in charge of "gasless transactions". | ||
*/ | ||
contract FastForwardableFacet is AFastFacet, AHasForwarder { | ||
/// AHasForwarder implementation. | ||
|
||
// For now the forwarder manager is an issuer. | ||
function isValidForwarderManager(address who) internal view override(AHasForwarder) returns (bool) { | ||
return _isIssuerMember(who); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go with an early return to get rid of the
else
statement. Possibly invert the condition to get what's in the currentelse
statement to return early.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yep true... honestly I was trying to modify this as little as possible from their implementation but we are already deviating from it so 🤷♂️
Original implementation here: https://github.com/opengsn/gsn/blob/v3.0.0-beta.10/packages/contracts/src/ERC2771Recipient.sol