Skip to content

Plugin updates #297

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

Merged
merged 3 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 0 additions & 13 deletions contracts/extension/interface/plugin/IEntrypointOverrideable.sol

This file was deleted.

39 changes: 33 additions & 6 deletions contracts/extension/interface/plugin/IMap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,43 @@
pragma solidity ^0.8.11;

interface IMap {
struct ExtensionMap {
/**
* @notice An interface to describe a plug-in functionality.
*
* @param selector 4-byte function signature.
* @param pluginAddress Address of the contract containing the function.
* @param functionString Function representation as a string. E.g. "transfer(address,address,uint256)"
*/
struct Plugin {
bytes4 selector;
address extension;
address pluginAddress;
string functionString;
}

event ExtensionRegistered(bytes4 indexed selector, address indexed extension);
/// @dev Emitted when a functionality is added, or plugged-in.
event PluginAdded(bytes4 indexed selector, address indexed pluginAddress);

function getExtensionForFunction(bytes4 _selector) external view returns (address);
/// @dev Emitted when a functionality is updated or overridden.
event PluginUpdated(bytes4 indexed selector, address indexed oldPluginAddress, address indexed newPluginAddress);

function getAllFunctionsOfExtension(address _extension) external view returns (bytes4[] memory registered);
/// @dev Emitted when a functionality is removed.
event PluginRemoved(bytes4 indexed selector, address indexed pluginAddress);

function getAllRegistered() external view returns (ExtensionMap[] memory registered);
/// @dev Add functionality to the contract.
function addPlugin(Plugin memory _plugin) external;

/// @dev Update or override existing functionality.
function updatePlugin(Plugin memory _plugin) external;

/// @dev Remove existing functionality from the contract.
function removePlugin(bytes4 _selector) external;

/// @dev View address of the plugged-in functionality contract for a given function signature.
function getPluginForFunction(bytes4 _selector) external view returns (address);

/// @dev View all funtionality as list of function signatures.
function getAllFunctionsOfPlugin(address _pluginAddress) external view returns (bytes4[] memory);

/// @dev View all funtionality existing on the contract.
function getAllPlugins() external view returns (Plugin[] memory);
}
120 changes: 0 additions & 120 deletions contracts/extension/plugin/EntrypointOverrideable.sol

This file was deleted.

122 changes: 95 additions & 27 deletions contracts/extension/plugin/Map.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,129 @@ pragma solidity ^0.8.0;

import "../interface/plugin/IMap.sol";

import "../Multicall.sol";
import "../../openzeppelin-presets/utils/EnumerableSet.sol";

/**
* TODO:
* - Remove OZ EnumerableSet external dependency.
*/

contract Map is IMap, Multicall {
abstract contract Map is IMap {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comments demarcating different sections of the contract like in e.g. Router.

using EnumerableSet for EnumerableSet.Bytes32Set;

EnumerableSet.Bytes32Set private allSelectors;

mapping(address => EnumerableSet.Bytes32Set) private selectorsForExtension;
mapping(bytes4 => address) private extension;
mapping(address => EnumerableSet.Bytes32Set) private selectorsForPlugin;
mapping(bytes4 => Plugin) private pluginForSelector;

/*///////////////////////////////////////////////////////////////
Constructor + initializer logic
//////////////////////////////////////////////////////////////*/

constructor(ExtensionMap[] memory _extensionsToRegister) {
uint256 len = _extensionsToRegister.length;
constructor(Plugin[] memory _pluginsToAdd) {
uint256 len = _pluginsToAdd.length;
for (uint256 i = 0; i < len; i += 1) {
_setExtensions(_extensionsToRegister[i].selector, _extensionsToRegister[i].extension);
_addPlugin(_pluginsToAdd[i]);
}
}

function _setExtensions(bytes4 _selector, address _extension) internal {
require(allSelectors.add(bytes32(_selector)), "REGISTERED");
/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/

/// @dev Add functionality to the contract.
function addPlugin(Plugin memory _plugin) external {
require(_canSetPlugin(), "Map: Not authorized");

_addPlugin(_plugin);
}

/// @dev Update or override existing functionality.
function updatePlugin(Plugin memory _plugin) external {
require(_canSetPlugin(), "Map: Not authorized");

extension[_selector] = _extension;
selectorsForExtension[_extension].add(bytes32(_selector));
_updatePlugin(_plugin);
}

/// @dev Remove existing functionality from the contract.
function removePlugin(bytes4 _selector) external {
require(_canSetPlugin(), "Map: Not authorized");

emit ExtensionRegistered(_selector, _extension);
_removePlugin(_selector);
}

function getExtensionForFunction(bytes4 _selector) external view returns (address) {
address ext = extension[_selector];
require(ext != address(0), "No extension available for selector.");
/*///////////////////////////////////////////////////////////////
View functions
//////////////////////////////////////////////////////////////*/

/// @dev View address of the plugged-in functionality contract for a given function signature.
function getPluginForFunction(bytes4 _selector) public view returns (address) {
address _pluginAddress = pluginForSelector[_selector].pluginAddress;
require(_pluginAddress != address(0), "Map: No plugin available for selector");

return ext;
return _pluginAddress;
}

function getAllFunctionsOfExtension(address _extension) external view returns (bytes4[] memory registered) {
uint256 len = selectorsForExtension[_extension].length();
/// @dev View all funtionality as list of function signatures.
function getAllFunctionsOfPlugin(address _pluginAddress) external view returns (bytes4[] memory registered) {
uint256 len = selectorsForPlugin[_pluginAddress].length();
registered = new bytes4[](len);

for (uint256 i = 0; i < len; i += 1) {
registered[i] = bytes4(selectorsForExtension[_extension].at(i));
registered[i] = bytes4(selectorsForPlugin[_pluginAddress].at(i));
}
}

function getAllRegistered() external view returns (ExtensionMap[] memory functionExtensionPairs) {
/// @dev View all funtionality existing on the contract.
function getAllPlugins() external view returns (Plugin[] memory _plugins) {
uint256 len = allSelectors.length();
functionExtensionPairs = new ExtensionMap[](len);
_plugins = new Plugin[](len);

for (uint256 i = 0; i < len; i += 1) {
bytes4 selector = bytes4(allSelectors.at(i));
functionExtensionPairs[i] = ExtensionMap(selector, extension[selector]);
_plugins[i] = pluginForSelector[selector];
}
}

/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/

/// @dev Add functionality to the contract.
function _addPlugin(Plugin memory _plugin) internal {
require(allSelectors.add(bytes32(_plugin.selector)), "Map: Selector exists");
require(
_plugin.selector == bytes4(keccak256(abi.encodePacked(_plugin.functionString))),
"Map: Incorrect selector"
);

pluginForSelector[_plugin.selector] = _plugin;
selectorsForPlugin[_plugin.pluginAddress].add(bytes32(_plugin.selector));

emit PluginAdded(_plugin.selector, _plugin.pluginAddress);
}
Comment on lines +90 to +101

Check warning

Code scanning / Slither

Unused return

Map._addPlugin(IMap.Plugin) (contracts/extension/plugin/Map.sol#90-101) ignores return value by selectorsForPlugin[_plugin.pluginAddress].add(bytes32(_plugin.selector)) (contracts/extension/plugin/Map.sol#98)

/// @dev Update or override existing functionality.
function _updatePlugin(Plugin memory _plugin) internal {
address currentPlugin = getPluginForFunction(_plugin.selector);
require(
_plugin.selector == bytes4(keccak256(abi.encodePacked(_plugin.functionString))),
"Map: Incorrect selector"
);

pluginForSelector[_plugin.selector] = _plugin;
selectorsForPlugin[currentPlugin].remove(bytes32(_plugin.selector));
selectorsForPlugin[_plugin.pluginAddress].add(bytes32(_plugin.selector));

emit PluginUpdated(_plugin.selector, currentPlugin, _plugin.pluginAddress);
}
Comment on lines +104 to +116

Check warning

Code scanning / Slither

Unused return

Map._updatePlugin(IMap.Plugin) (contracts/extension/plugin/Map.sol#104-116) ignores return value by selectorsForPlugin[currentPlugin].remove(bytes32(_plugin.selector)) (contracts/extension/plugin/Map.sol#112)
Comment on lines +104 to +116

Check warning

Code scanning / Slither

Unused return

Map._updatePlugin(IMap.Plugin) (contracts/extension/plugin/Map.sol#104-116) ignores return value by selectorsForPlugin[_plugin.pluginAddress].add(bytes32(_plugin.selector)) (contracts/extension/plugin/Map.sol#113)

/// @dev Remove existing functionality from the contract.
function _removePlugin(bytes4 _selector) internal {
address currentPlugin = getPluginForFunction(_selector);

delete pluginForSelector[_selector];
allSelectors.remove(_selector);
selectorsForPlugin[currentPlugin].remove(bytes32(_selector));

emit PluginRemoved(_selector, currentPlugin);
}
Comment on lines +119 to +127

Check warning

Code scanning / Slither

Unused return

Map._removePlugin(bytes4) (contracts/extension/plugin/Map.sol#119-127) ignores return value by selectorsForPlugin[currentPlugin].remove(bytes32(_selector)) (contracts/extension/plugin/Map.sol#124)
Comment on lines +119 to +127

Check warning

Code scanning / Slither

Unused return

Map._removePlugin(bytes4) (contracts/extension/plugin/Map.sol#119-127) ignores return value by allSelectors.remove(_selector) (contracts/extension/plugin/Map.sol#123)

/// @dev Returns whether plug-in can be set in the given execution context.
function _canSetPlugin() internal view virtual returns (bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,34 @@ pragma solidity ^0.8.0;

import "../interface/plugin/IMap.sol";
import "../../extension/Multicall.sol";
import "../../eip/ERC165.sol";
import "./Map.sol";

contract Entrypoint is Multicall {
abstract contract Router is Multicall, Map, ERC165 {
/*///////////////////////////////////////////////////////////////
State variables
Constructor + initializer logic
//////////////////////////////////////////////////////////////*/

address public immutable functionMap;
constructor(Plugin[] memory _pluginsToAdd) Map(_pluginsToAdd) {}

/*///////////////////////////////////////////////////////////////
Constructor + initializer logic
ERC 165
//////////////////////////////////////////////////////////////*/

constructor(address _functionMap) {
functionMap = _functionMap;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IMap).interfaceId || super.supportsInterface(interfaceId);
}

/*///////////////////////////////////////////////////////////////
Generic contract logic
//////////////////////////////////////////////////////////////*/

fallback() external payable virtual {
address extension = IMap(functionMap).getExtensionForFunction(msg.sig);
_delegate(extension);
address _pluginAddress = getPluginForFunction(msg.sig);
_delegate(_pluginAddress);
}

receive() external payable {}
Expand Down
Loading