Skip to content
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

Add Context contracts #285

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ccffe67
Add _msgSender() trick
danfinlay Aug 22, 2022
a0b566e
Move msgSender trick to utils file
danfinlay Aug 26, 2022
3e876b3
Bring in the better documented version of _msgSender
danfinlay Aug 26, 2022
ec749a3
Merge branch 'master' of github.com:delegatable/solidstate-solidity i…
ItsNickBarry Mar 17, 2025
b10834c
Merge branch 'master' into msg-sender-trick
ItsNickBarry Mar 17, 2025
bbb0b61
fix pragma statement
ItsNickBarry Mar 17, 2025
63366d2
update MsgSenderTrick to comply with layers pattern
ItsNickBarry Mar 17, 2025
8d8c13d
Merge branch 'master' into msg-sender-trick
ItsNickBarry Mar 17, 2025
29b7129
Merge branch 'master' into msg-sender-trick
ItsNickBarry Mar 18, 2025
9a0af0a
rename MsgSenderTrick to Context
ItsNickBarry Mar 18, 2025
6c93a45
expand _Context to _MetaTransactionContext
ItsNickBarry Mar 18, 2025
caf52fc
add ERC2771 interfaces and storage library
ItsNickBarry Mar 18, 2025
93d35e2
remove _getRelayedCallSender helper
ItsNickBarry Mar 18, 2025
645bcb4
add msg.data processing functions to Context
ItsNickBarry Mar 18, 2025
df41fde
use super calls to Context
ItsNickBarry Mar 18, 2025
5302758
remove Context comments
ItsNickBarry Mar 18, 2025
4fd84f1
remove assembly in _msgSender
ItsNickBarry Mar 18, 2025
78c875d
add Context comments
ItsNickBarry Mar 18, 2025
588aea2
rename MetaTransactionContext to ForwardedMetaTransactionContext
ItsNickBarry Mar 18, 2025
e13a3e2
conform ForwardedMetaTransactionContext to ERC2771
ItsNickBarry Mar 18, 2025
d71154c
add comments to IERC2771
ItsNickBarry Mar 18, 2025
1cb92ff
check data length before returning context
ItsNickBarry Mar 18, 2025
0466d01
add note about non-revert case and NatSpec comments
ItsNickBarry Mar 18, 2025
9290760
consider only contracts to be trusted forwarders
ItsNickBarry Mar 19, 2025
26c7c1b
add trusted forwarder status accessors
ItsNickBarry Mar 19, 2025
f0d86c5
Merge branch 'master' into msg-sender-trick
ItsNickBarry Mar 19, 2025
3904c44
add external Context contract
ItsNickBarry Mar 19, 2025
9350300
fix contract name
ItsNickBarry Mar 19, 2025
0d4aa5a
test Context contract
ItsNickBarry Mar 19, 2025
02f62f9
test ForwardedMetaTransactionContext contract
ItsNickBarry Mar 19, 2025
c30c631
move context contracts to meta directory
ItsNickBarry Mar 19, 2025
3c3a05d
move context tests to meta directory
ItsNickBarry Mar 20, 2025
6da28f6
fix test suite title
ItsNickBarry Mar 20, 2025
8797018
add callMetaTransaction test helper
ItsNickBarry Mar 20, 2025
9dc0673
enable arguments for callMetaTransaction helper
ItsNickBarry Mar 20, 2025
1750e80
test ForwardedMetaTransactionContext case where suffix is added but i…
ItsNickBarry Mar 20, 2025
059c414
allow EOA trusted forwarders
ItsNickBarry Mar 22, 2025
b87a95d
remove dev note
ItsNickBarry Mar 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions contracts/access/access_control/_AccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { EnumerableSet } from '../../data/EnumerableSet.sol';
import { AccessControlStorage } from '../../storage/AccessControlStorage.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { UintUtils } from '../../utils/UintUtils.sol';
import { _Context } from '../../meta/_Context.sol';
import { _IAccessControl } from './_IAccessControl.sol';

/**
* @title Role-based access control system
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
*/
abstract contract _AccessControl is _IAccessControl {
abstract contract _AccessControl is _IAccessControl, _Context {
using AddressUtils for address;
using EnumerableSet for EnumerableSet.AddressSet;
using UintUtils for uint256;
Expand Down Expand Up @@ -45,7 +46,7 @@ abstract contract _AccessControl is _IAccessControl {
* @param role role to query
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, msg.sender);
_checkRole(role, _msgSender());
}

/**
Expand Down Expand Up @@ -117,7 +118,7 @@ abstract contract _AccessControl is _IAccessControl {
* @param role role to relinquish
*/
function _renounceRole(bytes32 role) internal virtual {
_setRole(role, msg.sender, false);
_setRole(role, _msgSender(), false);
}

function _setRole(
Expand All @@ -131,14 +132,14 @@ abstract contract _AccessControl is _IAccessControl {
.roles[role]
.members
.add(account);
emit RoleGranted(role, account, msg.sender);
emit RoleGranted(role, account, _msgSender());
} else {
AccessControlStorage
.layout(AccessControlStorage.DEFAULT_STORAGE_SLOT)
.roles[role]
.members
.remove(account);
emit RoleRevoked(role, account, msg.sender);
emit RoleRevoked(role, account, _msgSender());
}
}

Expand Down
4 changes: 3 additions & 1 deletion contracts/access/access_control/_IAccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

pragma solidity ^0.8.20;

import { _IContext } from '../../meta/_IContext.sol';

/**
* @title Partial AccessControl interface needed by internal functions
*/
interface _IAccessControl {
interface _IAccessControl is _IContext {
event RoleAdminChanged(
bytes32 indexed role,
bytes32 indexed previousAdminRole,
Expand Down
3 changes: 2 additions & 1 deletion contracts/access/ownable/_IOwnable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
pragma solidity ^0.8.20;

import { _IERC173 } from '../../interfaces/_IERC173.sol';
import { _IContext } from '../../meta/_IContext.sol';

interface _IOwnable is _IERC173 {
interface _IOwnable is _IERC173, _IContext {
error Ownable__NotOwner();
error Ownable__NotTransitiveOwner();
}
7 changes: 4 additions & 3 deletions contracts/access/ownable/_Ownable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ pragma solidity ^0.8.20;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { ERC173Storage } from '../../storage/ERC173Storage.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { _Context } from '../../meta/_Context.sol';
import { _IOwnable } from './_IOwnable.sol';

abstract contract _Ownable is _IOwnable {
abstract contract _Ownable is _IOwnable, _Context {
using AddressUtils for address;

modifier onlyOwner() {
if (msg.sender != _owner()) revert Ownable__NotOwner();
if (_msgSender() != _owner()) revert Ownable__NotOwner();
_;
}

modifier onlyTransitiveOwner() {
if (msg.sender != _transitiveOwner())
if (_msgSender() != _transitiveOwner())
revert Ownable__NotTransitiveOwner();
_;
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/access/ownable/_SafeOwnable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { _Ownable } from './_Ownable.sol';

abstract contract _SafeOwnable is _ISafeOwnable, _Ownable {
modifier onlyNomineeOwner() {
if (msg.sender != _nomineeOwner())
if (_msgSender() != _nomineeOwner())
revert SafeOwnable__NotNomineeOwner();
_;
}
Expand All @@ -27,7 +27,7 @@ abstract contract _SafeOwnable is _ISafeOwnable, _Ownable {
* @notice accept transfer of contract ownership
*/
function _acceptOwnership() internal virtual onlyNomineeOwner {
_setOwner(msg.sender);
_setOwner(_msgSender());
delete ERC173Storage
.layout(ERC173Storage.DEFAULT_STORAGE_SLOT)
.nomineeOwner;
Expand Down
20 changes: 20 additions & 0 deletions contracts/interfaces/IERC2771.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { _IERC2771 } from './_IERC2771.sol';

/**
* @title ERC2771 interface
* @dev see https://eips.ethereum.org/EIPS/eip-2771
*/
interface IERC2771 is _IERC2771 {
/**
* @notice query whether account is a trusted ERC2771 forwarder
* @param account address to query
* @return trustedStatus whether account is a trusted forwarder
*/
function isTrustedForwarder(
address account
) external view returns (bool trustedStatus);
}
5 changes: 5 additions & 0 deletions contracts/interfaces/_IERC2771.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

interface _IERC2771 {}
8 changes: 8 additions & 0 deletions contracts/meta/Context.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { IContext } from './IContext.sol';
import { _Context } from './_Context.sol';

abstract contract Context is IContext, _Context {}
63 changes: 63 additions & 0 deletions contracts/meta/ForwardedMetaTransactionContext.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { IERC2771 } from '../interfaces/IERC2771.sol';
import { Context } from './Context.sol';
import { _Context } from './_Context.sol';
import { IForwardedMetaTransactionContext } from './IForwardedMetaTransactionContext.sol';
import { _ForwardedMetaTransactionContext } from './_ForwardedMetaTransactionContext.sol';

abstract contract ForwardedMetaTransactionContext is
IForwardedMetaTransactionContext,
_ForwardedMetaTransactionContext,
Context
{
/**
* @inheritdoc IERC2771
*/
function isTrustedForwarder(
address account
) external view returns (bool trustedStatus) {
trustedStatus = _isTrustedForwarder(account);
}

/**
* @inheritdoc _ForwardedMetaTransactionContext
*/
function _msgSender()
internal
view
virtual
override(_Context, _ForwardedMetaTransactionContext)
returns (address msgSender)
{
msgSender = super._msgSender();
}

/**
* @inheritdoc _ForwardedMetaTransactionContext
*/
function _msgData()
internal
view
virtual
override(_Context, _ForwardedMetaTransactionContext)
returns (bytes calldata msgData)
{
msgData = super._msgData();
}

/**
* @inheritdoc _ForwardedMetaTransactionContext
*/
function _calldataSuffixLength()
internal
view
virtual
override(_Context, _ForwardedMetaTransactionContext)
returns (uint256 length)
{
length = super._calldataSuffixLength();
}
}
7 changes: 7 additions & 0 deletions contracts/meta/IContext.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { _IContext } from './_IContext.sol';

interface IContext is _IContext {}
13 changes: 13 additions & 0 deletions contracts/meta/IForwardedMetaTransactionContext.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { IERC2771 } from '../interfaces/IERC2771.sol';
import { IContext } from './IContext.sol';
import { _IForwardedMetaTransactionContext } from './_IForwardedMetaTransactionContext.sol';

interface IForwardedMetaTransactionContext is
_IForwardedMetaTransactionContext,
IContext,
IERC2771
{}
42 changes: 42 additions & 0 deletions contracts/meta/_Context.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { _IContext } from './_IContext.sol';

/**
* @title Utility contract for supporting alternative authorization schemes
*/
abstract contract _Context is _IContext {
/**
* @notice return the message sender
* @dev if no Context extension is in use, msg.sender is returned as-is
* @return msgSender account contextualized as message sender
*/
function _msgSender() internal view virtual returns (address msgSender) {
msgSender = msg.sender;
}

/**
* @notice return the message data
* @dev if no Context extension is in use, msg.data is returned as-is
* @return msgData message data with suffix removed, if applicable
*/
function _msgData() internal view virtual returns (bytes calldata msgData) {
msgData = msg.data;
}

/**
* @notice return the bytes length of the calldata context suffix
* @dev if no Context extension is in use, suffix length is 0
* @return length length of calldata context suffix
*/
function _calldataSuffixLength()
internal
view
virtual
returns (uint256 length)
{
length = 0;
}
}
115 changes: 115 additions & 0 deletions contracts/meta/_ForwardedMetaTransactionContext.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { ERC2771Storage } from '../storage/ERC2771Storage.sol';
import { AddressUtils } from '../utils/AddressUtils.sol';
import { _Context } from './_Context.sol';
import { _IForwardedMetaTransactionContext } from './_IForwardedMetaTransactionContext.sol';

abstract contract _ForwardedMetaTransactionContext is
_IForwardedMetaTransactionContext,
_Context
{
using AddressUtils for address;

/**
* @inheritdoc _Context
* @dev sender is read from the calldata context suffix
*/
function _msgSender()
internal
view
virtual
override
returns (address msgSender)
{
uint256 dataLength = msg.data.length;
uint256 suffixLength = _calldataSuffixLength();

// ideally this would revert when dataLength < suffixLength and sender is a trusted forwarder
// but because _isTrustedForwarder reads from storage, it should be called only when necessary

if (dataLength >= suffixLength && _isTrustedForwarder(msg.sender)) {
unchecked {
msgSender = address(
bytes20(msg.data[dataLength - suffixLength:])
);
}
} else {
msgSender = super._msgSender();
}
}

/**
* @inheritdoc _Context
*/
function _msgData()
internal
view
virtual
override
returns (bytes calldata msgData)
{
uint256 dataLength = msg.data.length;
uint256 suffixLength = _calldataSuffixLength();

// ideally this would revert when dataLength < suffixLength and sender is a trusted forwarder
// but because _isTrustedForwarder reads from storage, it should be called only when necessary

if (dataLength >= suffixLength && _isTrustedForwarder(msg.sender)) {
unchecked {
msgData = msg.data[:dataLength - suffixLength];
}
} else {
msgData = super._msgData();
}
}

/**
* @inheritdoc _Context
* @dev this Context extension defines an address suffix with a length of 20
*/
function _calldataSuffixLength()
internal
view
virtual
override
returns (uint256 length)
{
length = 20;
}

/**
* @notice query whether account is a trusted ERC2771 forwarder
* @param account address to query
* @return trustedStatus whether account is a trusted forwarder
*/
function _isTrustedForwarder(
address account
) internal view virtual returns (bool trustedStatus) {
trustedStatus = ERC2771Storage
.layout(ERC2771Storage.DEFAULT_STORAGE_SLOT)
.trustedForwarders[account];
}

/**
* @notice grant trusted forwarder status to account
* @param account account whose trusted forwarder status to grant
*/
function _addTrustedForwarder(address account) internal virtual {
ERC2771Storage
.layout(ERC2771Storage.DEFAULT_STORAGE_SLOT)
.trustedForwarders[account] = true;
}

/**
* @notice revoke trusted forwarder status from account
* @param account account whose trusted forwarder status to revoke
*/
function _removeTrustedForwarder(address account) internal virtual {
delete ERC2771Storage
.layout(ERC2771Storage.DEFAULT_STORAGE_SLOT)
.trustedForwarders[account];
}
}
Loading