-
Notifications
You must be signed in to change notification settings - Fork 529
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MultichainRegistry: new code pattern (#285)
* MultichainRegistry: new code pattern * rename logic contracts * docs * v3.3.0-3 * v3.3.0-4 * IEntrypoint interface * update with new plugin design * v3.3.0-5 * update multichain registry with new plugin pattern * remove duplicate interface * forge update
- Loading branch information
1 parent
3f61125
commit 365bf69
Showing
29 changed files
with
2,230 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
contracts/registry/entrypoint/TWMultichainRegistryRouter.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
// ========== Internal imports ========== | ||
|
||
import "../extension/PermissionsEnumerableLogic.sol"; | ||
import "../extension/ERC2771ContextLogic.sol"; | ||
import "../../extension/Multicall.sol"; | ||
import "../../extension/plugin/Router.sol"; | ||
|
||
/** | ||
* | ||
* "Inherited by entrypoint" extensions. | ||
* - PermissionsEnumerable | ||
* - ERC2771Context | ||
* - Multicall | ||
* | ||
* "NOT inherited by entrypoint" extensions. | ||
* - TWMultichainRegistry | ||
*/ | ||
|
||
contract TWMultichainRegistryRouter is PermissionsEnumerableLogic, ERC2771ContextLogic, Router { | ||
/*/////////////////////////////////////////////////////////////// | ||
Constructor + initializer logic | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
constructor(address _pluginMap, address[] memory _trustedForwarders) | ||
ERC2771ContextLogic(_trustedForwarders) | ||
Router(_pluginMap) | ||
{ | ||
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
Overridable Permissions | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @dev Returns whether plug-in can be set in the given execution context. | ||
function _canSetPlugin() internal view override returns (bool) { | ||
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
function _msgSender() internal view override(ERC2771ContextLogic, PermissionsLogic) returns (address sender) { | ||
if (isTrustedForwarder(msg.sender)) { | ||
// The assembly code is more direct than the Solidity version using `abi.decode`. | ||
assembly { | ||
sender := shr(96, calldataload(sub(calldatasize(), 20))) | ||
} | ||
} else { | ||
return msg.sender; | ||
} | ||
} | ||
|
||
function _msgData() internal view override(ERC2771ContextLogic, PermissionsLogic) returns (bytes calldata) { | ||
if (isTrustedForwarder(msg.sender)) { | ||
return msg.data[:msg.data.length - 20]; | ||
} else { | ||
return msg.data; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import "./ERC2771ContextLogic.sol"; | ||
|
||
interface IERC2771Context { | ||
function isTrustedForwarder(address forwarder) external view returns (bool); | ||
} | ||
|
||
/** | ||
* @dev Context variant with ERC2771 support. | ||
*/ | ||
abstract contract ERC2771ContextConsumer { | ||
function _msgSender() public view virtual returns (address sender) { | ||
if (IERC2771Context(address(this)).isTrustedForwarder(msg.sender)) { | ||
// The assembly code is more direct than the Solidity version using `abi.decode`. | ||
assembly { | ||
sender := shr(96, calldataload(sub(calldatasize(), 20))) | ||
} | ||
} else { | ||
return msg.sender; | ||
} | ||
} | ||
|
||
function _msgData() public view virtual returns (bytes calldata) { | ||
if (IERC2771Context(address(this)).isTrustedForwarder(msg.sender)) { | ||
return msg.data[:msg.data.length - 20]; | ||
} else { | ||
return msg.data; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import "./ERC2771ContextStorage.sol"; | ||
|
||
/** | ||
* @dev Context variant with ERC2771 support. | ||
*/ | ||
abstract contract ERC2771ContextLogic { | ||
constructor(address[] memory trustedForwarder) { | ||
ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage(); | ||
|
||
for (uint256 i = 0; i < trustedForwarder.length; i++) { | ||
data._trustedForwarder[trustedForwarder[i]] = true; | ||
} | ||
} | ||
|
||
function isTrustedForwarder(address forwarder) public view virtual returns (bool) { | ||
ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage(); | ||
return data._trustedForwarder[forwarder]; | ||
} | ||
|
||
function _msgSender() internal view virtual returns (address sender) { | ||
if (isTrustedForwarder(msg.sender)) { | ||
// The assembly code is more direct than the Solidity version using `abi.decode`. | ||
assembly { | ||
sender := shr(96, calldataload(sub(calldatasize(), 20))) | ||
} | ||
} else { | ||
return msg.sender; | ||
} | ||
} | ||
|
||
function _msgData() internal view virtual returns (bytes calldata) { | ||
if (isTrustedForwarder(msg.sender)) { | ||
return msg.data[:msg.data.length - 20]; | ||
} else { | ||
return msg.data; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
library ERC2771ContextStorage { | ||
bytes32 public constant ERC2771_CONTEXT_STORAGE_POSITION = keccak256("erc2771.context.storage"); | ||
|
||
struct Data { | ||
mapping(address => bool) _trustedForwarder; | ||
} | ||
|
||
function erc2771ContextStorage() internal pure returns (Data storage erc2771ContextData) { | ||
bytes32 position = ERC2771_CONTEXT_STORAGE_POSITION; | ||
assembly { | ||
erc2771ContextData.slot := position | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
interface IContext { | ||
function _msgSender() external view returns (address sender); | ||
|
||
function _msgData() external view returns (bytes calldata); | ||
} |
97 changes: 97 additions & 0 deletions
97
contracts/registry/extension/PermissionsEnumerableLogic.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import "./PermissionsEnumerableStorage.sol"; | ||
import "./PermissionsLogic.sol"; | ||
|
||
/** | ||
* @title PermissionsEnumerable | ||
* @dev This contracts provides extending-contracts with role-based access control mechanisms. | ||
* Also provides interfaces to view all members with a given role, and total count of members. | ||
*/ | ||
contract PermissionsEnumerableLogic is IPermissionsEnumerable, PermissionsLogic { | ||
/** | ||
* @notice Returns the role-member from a list of members for a role, | ||
* at a given index. | ||
* @dev Returns `member` who has `role`, at `index` of role-members list. | ||
* See struct {RoleMembers}, and mapping {roleMembers} | ||
* | ||
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") | ||
* @param index Index in list of current members for the role. | ||
* | ||
* @return member Address of account that has `role` | ||
*/ | ||
function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) { | ||
PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage(); | ||
uint256 currentIndex = data.roleMembers[role].index; | ||
uint256 check; | ||
|
||
for (uint256 i = 0; i < currentIndex; i += 1) { | ||
if (data.roleMembers[role].members[i] != address(0)) { | ||
if (check == index) { | ||
member = data.roleMembers[role].members[i]; | ||
return member; | ||
} | ||
check += 1; | ||
} else if (hasRole(role, address(0)) && i == data.roleMembers[role].indexOf[address(0)]) { | ||
check += 1; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @notice Returns total number of accounts that have a role. | ||
* @dev Returns `count` of accounts that have `role`. | ||
* See struct {RoleMembers}, and mapping {roleMembers} | ||
* | ||
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE") | ||
* | ||
* @return count Total number of accounts that have `role` | ||
*/ | ||
function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) { | ||
PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage(); | ||
uint256 currentIndex = data.roleMembers[role].index; | ||
|
||
for (uint256 i = 0; i < currentIndex; i += 1) { | ||
if (data.roleMembers[role].members[i] != address(0)) { | ||
count += 1; | ||
} | ||
} | ||
if (hasRole(role, address(0))) { | ||
count += 1; | ||
} | ||
} | ||
|
||
/// @dev Revokes `role` from `account`, and removes `account` from {roleMembers} | ||
/// See {_removeMember} | ||
function _revokeRole(bytes32 role, address account) internal override { | ||
super._revokeRole(role, account); | ||
_removeMember(role, account); | ||
} | ||
|
||
/// @dev Grants `role` to `account`, and adds `account` to {roleMembers} | ||
/// See {_addMember} | ||
function _setupRole(bytes32 role, address account) internal override { | ||
super._setupRole(role, account); | ||
_addMember(role, account); | ||
} | ||
|
||
/// @dev adds `account` to {roleMembers}, for `role` | ||
function _addMember(bytes32 role, address account) internal { | ||
PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage(); | ||
uint256 idx = data.roleMembers[role].index; | ||
data.roleMembers[role].index += 1; | ||
|
||
data.roleMembers[role].members[idx] = account; | ||
data.roleMembers[role].indexOf[account] = idx; | ||
} | ||
|
||
/// @dev removes `account` from {roleMembers}, for `role` | ||
function _removeMember(bytes32 role, address account) internal { | ||
PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage(); | ||
uint256 idx = data.roleMembers[role].indexOf[account]; | ||
|
||
delete data.roleMembers[role].members[idx]; | ||
delete data.roleMembers[role].indexOf[account]; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
contracts/registry/extension/PermissionsEnumerableStorage.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import "../../extension/interface/IPermissionsEnumerable.sol"; | ||
|
||
library PermissionsEnumerableStorage { | ||
bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION = keccak256("permissions.enumerable.storage"); | ||
|
||
/** | ||
* @notice A data structure to store data of members for a given role. | ||
* | ||
* @param index Current index in the list of accounts that have a role. | ||
* @param members map from index => address of account that has a role | ||
* @param indexOf map from address => index which the account has. | ||
*/ | ||
struct RoleMembers { | ||
uint256 index; | ||
mapping(uint256 => address) members; | ||
mapping(address => uint256) indexOf; | ||
} | ||
|
||
struct Data { | ||
/// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}. | ||
mapping(bytes32 => RoleMembers) roleMembers; | ||
} | ||
|
||
function permissionsEnumerableStorage() internal pure returns (Data storage permissionsEnumerableData) { | ||
bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION; | ||
assembly { | ||
permissionsEnumerableData.slot := position | ||
} | ||
} | ||
} |
Oops, something went wrong.