Skip to content

Commit

Permalink
Add EntrypointOverrideable: opt-in override an extension for a functi…
Browse files Browse the repository at this point in the history
…on. (#293)
  • Loading branch information
nkrishang authored Dec 11, 2022
1 parent 4a7c7fa commit 267383e
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 0 deletions.
13 changes: 13 additions & 0 deletions contracts/extension/interface/plugin/IEntrypointOverrideable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

interface IEntrypointOverrideable {
struct ExtensionMap {
bytes4 selector;
address extension;
}

function overrideExtensionForFunction(bytes4 _selector, address _extension) external;

function getAllOverriden() external view returns (ExtensionMap[] memory functionExtensionPairs);
}
120 changes: 120 additions & 0 deletions contracts/extension/plugin/EntrypointOverrideable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "../interface/plugin/IMap.sol";
import "../interface/plugin/IEntrypointOverrideable.sol";
import "../../extension/Multicall.sol";
import "../../openzeppelin-presets/utils/EnumerableSet.sol";

library EntrypointOverrideableStorage {
bytes32 public constant ENTRYPOINT_OVERRIDEABLE_STORAGE_POSITION = keccak256("entrypoint.overrideable.storage");

struct Data {
EnumerableSet.Bytes32Set functions;
mapping(bytes4 => address) extensionOverride;
}

function entrypointStorage() internal pure returns (Data storage entrypointData) {
bytes32 position = ENTRYPOINT_OVERRIDEABLE_STORAGE_POSITION;
assembly {
entrypointData.slot := position
}
}
}

abstract contract EntrypointOverrideable is Multicall, IEntrypointOverrideable {
using EnumerableSet for EnumerableSet.Bytes32Set;

/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*/

address public immutable functionMap;

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

constructor(address _functionMap) {
functionMap = _functionMap;
}

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

fallback() external payable virtual {
address extension = _getExtensionOverride(msg.sig);
if (extension == address(0)) {
extension = IMap(functionMap).getExtensionForFunction(msg.sig);
}

_delegate(extension);
}

receive() external payable {}

function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())

// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

// Copy the returned data.
returndatacopy(0, 0, returndatasize())

switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}

/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/

function overrideExtensionForFunction(bytes4 _selector, address _extension) external {
require(_canOverrideExtensions(), "Entrypoint: cannot override extensions.");

EntrypointOverrideableStorage.Data storage data = EntrypointOverrideableStorage.entrypointStorage();
data.extensionOverride[_selector] = _extension;

if (_extension != address(0)) {
data.functions.add(bytes32(_selector));
} else {
data.functions.remove(bytes32(_selector));
}
}

function getAllOverriden() external view returns (ExtensionMap[] memory functionExtensionPairs) {
EntrypointOverrideableStorage.Data storage data = EntrypointOverrideableStorage.entrypointStorage();
uint256 len = data.functions.length();
functionExtensionPairs = new ExtensionMap[](len);

for (uint256 i = 0; i < len; i += 1) {
bytes4 selector = bytes4(data.functions.at(i));
functionExtensionPairs[i] = ExtensionMap(selector, data.extensionOverride[selector]);
}
}

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

function _getExtensionOverride(bytes4 _selector) internal view returns (address) {
EntrypointOverrideableStorage.Data storage data = EntrypointOverrideableStorage.entrypointStorage();
return data.extensionOverride[_selector];
}

function _canOverrideExtensions() internal view virtual returns (bool);
}
88 changes: 88 additions & 0 deletions docs/EntrypointOverrideable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# EntrypointOverrideable









## Methods

### functionMap

```solidity
function functionMap() external view returns (address)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | address | undefined |

### getAllOverriden

```solidity
function getAllOverriden() external view returns (struct IEntrypointOverrideable.ExtensionMap[] functionExtensionPairs)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| functionExtensionPairs | IEntrypointOverrideable.ExtensionMap[] | undefined |

### multicall

```solidity
function multicall(bytes[] data) external nonpayable returns (bytes[] results)
```

Receives and executes a batch of function calls on this contract.

*Receives and executes a batch of function calls on this contract.*

#### Parameters

| Name | Type | Description |
|---|---|---|
| data | bytes[] | The bytes data that makes up the batch of function calls to execute. |

#### Returns

| Name | Type | Description |
|---|---|---|
| results | bytes[] | The bytes data that makes up the result of the batch of function calls executed. |

### overrideExtensionForFunction

```solidity
function overrideExtensionForFunction(bytes4 _selector, address _extension) external nonpayable
```





#### Parameters

| Name | Type | Description |
|---|---|---|
| _selector | bytes4 | undefined |
| _extension | address | undefined |




32 changes: 32 additions & 0 deletions docs/EntrypointOverrideableStorage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# EntrypointOverrideableStorage









## Methods

### ENTRYPOINT_OVERRIDEABLE_STORAGE_POSITION

```solidity
function ENTRYPOINT_OVERRIDEABLE_STORAGE_POSITION() external view returns (bytes32)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| _0 | bytes32 | undefined |




49 changes: 49 additions & 0 deletions docs/IEntrypointOverrideable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# IEntrypointOverrideable









## Methods

### getAllOverriden

```solidity
function getAllOverriden() external view returns (struct IEntrypointOverrideable.ExtensionMap[] functionExtensionPairs)
```






#### Returns

| Name | Type | Description |
|---|---|---|
| functionExtensionPairs | IEntrypointOverrideable.ExtensionMap[] | undefined |

### overrideExtensionForFunction

```solidity
function overrideExtensionForFunction(bytes4 _selector, address _extension) external nonpayable
```





#### Parameters

| Name | Type | Description |
|---|---|---|
| _selector | bytes4 | undefined |
| _extension | address | undefined |




Loading

0 comments on commit 267383e

Please sign in to comment.