Skip to content

Commit 169ae05

Browse files
authored
Plugin architecture: Entrypoint (#292)
* Entrypoint for plugin architecture * Add test for Entrypoint
1 parent 6983f8c commit 169ae05

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "../interface/plugin/IMap.sol";
5+
import "../../extension/Multicall.sol";
6+
7+
contract Entrypoint is Multicall {
8+
/*///////////////////////////////////////////////////////////////
9+
State variables
10+
//////////////////////////////////////////////////////////////*/
11+
12+
address public immutable functionMap;
13+
14+
/*///////////////////////////////////////////////////////////////
15+
Constructor + initializer logic
16+
//////////////////////////////////////////////////////////////*/
17+
18+
constructor(address _functionMap) {
19+
functionMap = _functionMap;
20+
}
21+
22+
/*///////////////////////////////////////////////////////////////
23+
Generic contract logic
24+
//////////////////////////////////////////////////////////////*/
25+
26+
fallback() external payable virtual {
27+
address extension = IMap(functionMap).getExtensionForFunction(msg.sig);
28+
_delegate(extension);
29+
}
30+
31+
receive() external payable {}
32+
33+
function _delegate(address implementation) internal virtual {
34+
assembly {
35+
// Copy msg.data. We take full control of memory in this inline assembly
36+
// block because it will not return to Solidity code. We overwrite the
37+
// Solidity scratch pad at memory position 0.
38+
calldatacopy(0, 0, calldatasize())
39+
40+
// Call the implementation.
41+
// out and outsize are 0 because we don't know the size yet.
42+
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
43+
44+
// Copy the returned data.
45+
returndatacopy(0, 0, returndatasize())
46+
47+
switch result
48+
// delegatecall returns 0 on error.
49+
case 0 {
50+
revert(0, returndatasize())
51+
}
52+
default {
53+
return(0, returndatasize())
54+
}
55+
}
56+
}
57+
}

docs/Entrypoint.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Entrypoint
2+
3+
4+
5+
6+
7+
8+
9+
10+
11+
## Methods
12+
13+
### functionMap
14+
15+
```solidity
16+
function functionMap() external view returns (address)
17+
```
18+
19+
20+
21+
22+
23+
24+
#### Returns
25+
26+
| Name | Type | Description |
27+
|---|---|---|
28+
| _0 | address | undefined |
29+
30+
### multicall
31+
32+
```solidity
33+
function multicall(bytes[] data) external nonpayable returns (bytes[] results)
34+
```
35+
36+
Receives and executes a batch of function calls on this contract.
37+
38+
*Receives and executes a batch of function calls on this contract.*
39+
40+
#### Parameters
41+
42+
| Name | Type | Description |
43+
|---|---|---|
44+
| data | bytes[] | The bytes data that makes up the batch of function calls to execute. |
45+
46+
#### Returns
47+
48+
| Name | Type | Description |
49+
|---|---|---|
50+
| results | bytes[] | The bytes data that makes up the result of the batch of function calls executed. |
51+
52+
53+
54+

src/test/plugin/Entrypoint.t.sol

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "contracts/extension/plugin/Map.sol";
5+
import "contracts/extension/plugin/Entrypoint.sol";
6+
import { BaseTest } from "../utils/BaseTest.sol";
7+
8+
contract Counter {
9+
uint256 private number_;
10+
11+
function number() external view returns (uint256) {
12+
return number_;
13+
}
14+
15+
function setNumber(uint256 _newNum) external {
16+
number_ = _newNum;
17+
}
18+
19+
function doubleNumber() external {
20+
number_ *= 2;
21+
}
22+
}
23+
24+
contract EntrypointTest is BaseTest {
25+
address entrypoint;
26+
27+
function setUp() public override {
28+
super.setUp();
29+
30+
address counter = address(new Counter());
31+
32+
IMap.ExtensionMap[] memory extensionMaps = new IMap.ExtensionMap[](2);
33+
extensionMaps[0] = IMap.ExtensionMap(Counter.number.selector, counter);
34+
extensionMaps[1] = IMap.ExtensionMap(Counter.setNumber.selector, counter);
35+
36+
address map = address(new Map(extensionMaps));
37+
entrypoint = address(new Entrypoint(map));
38+
}
39+
40+
function test_state_callWithEntrypoint() external {
41+
uint256 num = 5;
42+
43+
Counter(entrypoint).setNumber(num);
44+
45+
assertEq(Counter(entrypoint).number(), num);
46+
}
47+
48+
function test_revert_callWithEntrypoint() external {
49+
vm.expectRevert("No extension available for selector.");
50+
Counter(entrypoint).doubleNumber();
51+
}
52+
}

0 commit comments

Comments
 (0)