Skip to content

Commit 3e40bab

Browse files
authored
Dynamic contracts (#345)
* forge update * forge install: dynamic-contracts v1.0.0 * dynamic contracts pattern setup * rename to dynamic-contracts
1 parent 51d459e commit 3e40bab

22 files changed

+3366
-3
lines changed

.gitmodules

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
url = https://github.com/brockelmore/forge-std
44
[submodule "lib/ds-test"]
55
path = lib/ds-test
6-
url = https://github.com/dapphub/ds-test
6+
url = https://github.com/dapphub/ds-test
7+
[submodule "lib/dynamic-contracts"]
8+
path = lib/dynamic-contracts
9+
url = https://github.com/thirdweb-dev/dynamic-contracts
10+
branch = v1.0.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
// Interface
5+
import "./interface/IExtensionRegistry.sol";
6+
7+
// Extensions
8+
import "../extension/PermissionsEnumerable.sol";
9+
import "lib/dynamic-contracts/src/presets/utils/ExtensionState.sol";
10+
import "lib/dynamic-contracts/src/presets/utils/StringSet.sol";
11+
12+
contract ExtensionRegistry is IExtensionRegistry, ExtensionState, PermissionsEnumerable {
13+
using StringSet for StringSet.Set;
14+
15+
/*///////////////////////////////////////////////////////////////
16+
Constructor
17+
//////////////////////////////////////////////////////////////*/
18+
19+
constructor(address _defaultAdmin) {
20+
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
21+
}
22+
23+
/*///////////////////////////////////////////////////////////////
24+
External functions
25+
//////////////////////////////////////////////////////////////*/
26+
27+
/// @notice Adds a new extension to the registry.
28+
function addExtension(Extension memory _extension) external onlyRole(DEFAULT_ADMIN_ROLE) {
29+
_addExtension(_extension);
30+
}
31+
32+
/// @notice Updates an existing extension in the registry.
33+
function updateExtension(Extension memory _extension) external onlyRole(DEFAULT_ADMIN_ROLE) {
34+
_updateExtension(_extension);
35+
}
36+
37+
/// @notice Remove an existing extension from the registry.
38+
function removeExtension(string memory _extensionName) external onlyRole(DEFAULT_ADMIN_ROLE) {
39+
_removeExtension(_extensionName);
40+
}
41+
42+
/*///////////////////////////////////////////////////////////////
43+
View functions
44+
//////////////////////////////////////////////////////////////*/
45+
46+
/// @notice Returns all extensions stored.
47+
function getAllExtensions() external view returns (Extension[] memory allExtensions) {
48+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
49+
50+
string[] memory names = data.extensionNames.values();
51+
uint256 len = names.length;
52+
53+
allExtensions = new Extension[](len);
54+
55+
for (uint256 i = 0; i < len; i += 1) {
56+
allExtensions[i] = data.extensions[names[i]];
57+
}
58+
}
59+
60+
/// @notice Returns the extension metadata and functions for a given extension.
61+
function getExtension(string memory _extensionName) public view returns (Extension memory) {
62+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
63+
require(data.extensionNames.contains(_extensionName), "ExtensionRegistry: extension does not exist.");
64+
return data.extensions[_extensionName];
65+
}
66+
67+
/// @notice Returns the extension's implementation smart contract address.
68+
function getExtensionImplementation(string memory _extensionName) external view returns (address) {
69+
return getExtension(_extensionName).metadata.implementation;
70+
}
71+
72+
/// @notice Returns all functions that belong to the given extension contract.
73+
function getAllFunctionsOfExtension(string memory _extensionName)
74+
external
75+
view
76+
returns (ExtensionFunction[] memory)
77+
{
78+
return getExtension(_extensionName).functions;
79+
}
80+
81+
/// @notice Returns the extension metadata for a given function.
82+
function getExtensionForFunction(bytes4 _functionSelector) external view returns (ExtensionMetadata memory) {
83+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
84+
ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
85+
require(metadata.implementation != address(0), "ExtensionRegistry: no extension for function.");
86+
return metadata;
87+
}
88+
}
+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
// Interface
5+
import "./interface/ITWRouter.sol";
6+
import "./interface/IExtensionRegistry.sol";
7+
8+
// Extensions & libraries
9+
import "../extension/Multicall.sol";
10+
11+
// Extension pattern imports
12+
import "lib/dynamic-contracts/src/presets/utils/StringSet.sol";
13+
import "lib/dynamic-contracts/src/core/Router.sol";
14+
import "lib/dynamic-contracts/src/presets/utils/DefaultExtensionSet.sol";
15+
import "lib/dynamic-contracts/src/presets/utils/ExtensionState.sol";
16+
17+
abstract contract TWRouter is ITWRouter, Multicall, ExtensionState, Router {
18+
using StringSet for StringSet.Set;
19+
20+
/*///////////////////////////////////////////////////////////////
21+
State variables
22+
//////////////////////////////////////////////////////////////*/
23+
24+
/// @notice The DefaultExtensionSet that stores default extensions of the router.
25+
address public immutable defaultExtensionSet;
26+
27+
/// @notice The ExtensionRegistry that stores all latest, vetted extensions available to router.
28+
address public immutable extensionRegistry;
29+
30+
/*///////////////////////////////////////////////////////////////
31+
Constructor
32+
//////////////////////////////////////////////////////////////*/
33+
34+
constructor(address _extensionRegistry, string[] memory _extensionNames) {
35+
extensionRegistry = _extensionRegistry;
36+
37+
DefaultExtensionSet map = new DefaultExtensionSet();
38+
defaultExtensionSet = address(map);
39+
40+
uint256 len = _extensionNames.length;
41+
42+
for (uint256 i = 0; i < len; i += 1) {
43+
Extension memory extension = IExtensionRegistry(_extensionRegistry).getExtension(_extensionNames[i]);
44+
map.setExtension(extension);
45+
}
46+
}
47+
48+
/*///////////////////////////////////////////////////////////////
49+
External functions
50+
//////////////////////////////////////////////////////////////*/
51+
52+
/// @dev Adds a new extension to the router.
53+
function addExtension(string memory _extensionName) external {
54+
require(_canSetExtension(), "TWRouter: caller not authorized");
55+
56+
Extension memory extension = IExtensionRegistry(extensionRegistry).getExtension(_extensionName);
57+
58+
_addExtension(extension);
59+
}
60+
61+
/// @dev Updates an existing extension in the router, or overrides a default extension.
62+
function updateExtension(string memory _extensionName) external {
63+
require(_canSetExtension(), "TWRouter: caller not authorized");
64+
65+
Extension memory extension = IExtensionRegistry(extensionRegistry).getExtension(_extensionName);
66+
67+
_updateExtension(extension);
68+
}
69+
70+
/// @dev Removes an existing extension from the router.
71+
function removeExtension(string memory _extensionName) external {
72+
require(_canSetExtension(), "TWRouter: caller not authorized");
73+
74+
_removeExtension(_extensionName);
75+
}
76+
77+
/*///////////////////////////////////////////////////////////////
78+
View functions
79+
//////////////////////////////////////////////////////////////*/
80+
81+
/**
82+
* @notice Returns all extensions stored. Override default lugins stored in router are
83+
* given precedence over default extensions in DefaultExtensionSet.
84+
*/
85+
function getAllExtensions() external view returns (Extension[] memory allExtensions) {
86+
Extension[] memory mapExtensions = IDefaultExtensionSet(defaultExtensionSet).getAllExtensions();
87+
uint256 mapExtensionsLen = mapExtensions.length;
88+
89+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
90+
string[] memory names = data.extensionNames.values();
91+
uint256 namesLen = names.length;
92+
93+
uint256 overrides = 0;
94+
for (uint256 i = 0; i < mapExtensionsLen; i += 1) {
95+
if (data.extensionNames.contains(mapExtensions[i].metadata.name)) {
96+
overrides += 1;
97+
}
98+
}
99+
100+
uint256 total = (namesLen + mapExtensionsLen) - overrides;
101+
102+
allExtensions = new Extension[](total);
103+
uint256 idx = 0;
104+
105+
for (uint256 i = 0; i < mapExtensionsLen; i += 1) {
106+
string memory name = mapExtensions[i].metadata.name;
107+
if (!data.extensionNames.contains(name)) {
108+
allExtensions[idx] = mapExtensions[i];
109+
idx += 1;
110+
}
111+
}
112+
113+
for (uint256 i = 0; i < namesLen; i += 1) {
114+
allExtensions[idx] = data.extensions[names[i]];
115+
idx += 1;
116+
}
117+
}
118+
119+
/// @dev Returns the extension metadata and functions for a given extension.
120+
function getExtension(string memory _extensionName) public view returns (Extension memory) {
121+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
122+
bool isLocalExtension = data.extensionNames.contains(_extensionName);
123+
124+
return
125+
isLocalExtension
126+
? data.extensions[_extensionName]
127+
: IDefaultExtensionSet(defaultExtensionSet).getExtension(_extensionName);
128+
}
129+
130+
/// @dev Returns the extension's implementation smart contract address.
131+
function getExtensionImplementation(string memory _extensionName) external view returns (address) {
132+
return getExtension(_extensionName).metadata.implementation;
133+
}
134+
135+
/// @dev Returns all functions that belong to the given extension contract.
136+
function getAllFunctionsOfExtension(string memory _extensionName)
137+
external
138+
view
139+
returns (ExtensionFunction[] memory)
140+
{
141+
return getExtension(_extensionName).functions;
142+
}
143+
144+
/// @dev Returns the Extension metadata for a given function.
145+
function getExtensionForFunction(bytes4 _functionSelector) public view returns (ExtensionMetadata memory) {
146+
ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
147+
ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
148+
149+
bool isLocalExtension = metadata.implementation != address(0);
150+
151+
return
152+
isLocalExtension
153+
? metadata
154+
: IDefaultExtensionSet(defaultExtensionSet).getExtensionForFunction(_functionSelector);
155+
}
156+
157+
/// @dev Returns the extension implementation address stored in router, for the given function.
158+
function getImplementationForFunction(bytes4 _functionSelector)
159+
public
160+
view
161+
override
162+
returns (address extensionAddress)
163+
{
164+
return getExtensionForFunction(_functionSelector).implementation;
165+
}
166+
167+
/*///////////////////////////////////////////////////////////////
168+
Internal functions
169+
//////////////////////////////////////////////////////////////*/
170+
171+
/// @dev Returns whether a extension can be set in the given execution context.
172+
function _canSetExtension() internal view virtual returns (bool);
173+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "lib/dynamic-contracts/src/interface/IDefaultExtensionSet.sol";
5+
6+
interface IExtensionRegistry is IDefaultExtensionSet {
7+
/*///////////////////////////////////////////////////////////////
8+
External functions
9+
//////////////////////////////////////////////////////////////*/
10+
11+
/// @dev Adds a new extension to the registry.
12+
function addExtension(Extension memory extension) external;
13+
14+
/// @dev Updates an existing extension in the registry.
15+
function updateExtension(Extension memory extension) external;
16+
17+
/// @dev Remove an existing extension from the registry.
18+
function removeExtension(string memory extension) external;
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
import "lib/dynamic-contracts/src/interface/IDefaultExtensionSet.sol";
5+
6+
interface ITWRouter is IDefaultExtensionSet {
7+
/*///////////////////////////////////////////////////////////////
8+
External functions
9+
//////////////////////////////////////////////////////////////*/
10+
11+
/// @dev Adds a new extension to the router.
12+
function addExtension(string memory extensionName) external;
13+
14+
/// @dev Updates an existing extension in the router, or overrides a default extension.
15+
function updateExtension(string memory extensionName) external;
16+
17+
/// @dev Removes an existing extension from the router.
18+
function removeExtension(string memory extensionName) external;
19+
}

0 commit comments

Comments
 (0)