Skip to content

Commit

Permalink
Plugin updates (#297)
Browse files Browse the repository at this point in the history
* plugin updates

* fix issue, code cleanup
  • Loading branch information
kumaryash90 authored Dec 21, 2022
1 parent 6179287 commit 4709d1e
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 280 deletions.
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 {
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);
}

/// @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);
}

/// @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);
}

/// @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

0 comments on commit 4709d1e

Please sign in to comment.