Skip to content

Commit 2e9b90f

Browse files
authored
Merge pull request #13 from violetprotocol/dev
v1.2.0 Andromeda
2 parents c2c36f7 + ab4db9f commit 2e9b90f

55 files changed

Lines changed: 1996 additions & 684 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ All contracts must inherit the _Extendable_ contract.
4949

5050
_Extendable_ contracts have a unique interaction with the _ExtendLogic_ contract where Extensions are added. _Extendable_ contracts access the state written by the _ExtendLogic_ extension in order to perform delegate calls to each extension. All calls are done from the context of the _Extendable_ contract which is handled by `delegatecall`.
5151

52-
_Extendable_ contracts have an evolving interface which is accessible through the `getCurrentInterface` function supplied by the _ExtendLogic_ extension. This allows developers to easily determine the current interface of an evolving _Extendable_ contract directly on-chain without having to query separate processes that may not be in sync (GitHub, Documentation, Twitter etc.).
52+
_Extendable_ contracts have an evolving interface which is accessible through the `getFullInterface` function supplied by the _ExtendLogic_ extension. This allows developers to easily determine the current interface of an evolving _Extendable_ contract directly on-chain without having to query separate processes that may not be in sync (GitHub, Documentation, Twitter etc.).
5353

5454
### Extensions
5555

@@ -85,7 +85,7 @@ struct YourState {
8585
library YourStorage {
8686
bytes32 constant private STORAGE_NAME = keccak256("your_unique_storage_identifier");
8787
88-
function _getStorage()
88+
function _getState()
8989
internal
9090
view
9191
returns (YourState storage state)
@@ -104,7 +104,7 @@ import "./YourStorage.sol";
104104
105105
contract YourExtension is IYourExtension, Extension {
106106
function readStorage() public view {
107-
YourState storage state = YourStorage._getStorage();
107+
YourState storage state = YourStorage._getState();
108108
109109
// access properties of state with `state.yourVar`
110110
// re-assign state properties with `state.yourVar = <value>`

contracts/erc165/ERC165Logic.sol

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "./IERC165Logic.sol";
5+
import { ERC165State, ERC165Storage } from "../storage/ERC165Storage.sol";
6+
7+
/**
8+
* @dev Standalone implementation of a global ERC165 interface detector.
9+
*
10+
* Deployed as singleton using EIP-2470: https://eips.ethereum.org/EIPS/eip-2470
11+
*
12+
* Provides ERC165 functionalities to any contract without it needing to
13+
* implement ERC165 itself.
14+
*
15+
* Allows it to be re-used by both Extensions and Extendables.
16+
*/
17+
contract ERC165Logic is IERC165, IERC165Register {
18+
/**
19+
* @dev Records its own contract address during construction
20+
*/
21+
address private self;
22+
constructor() {
23+
self = address(this);
24+
}
25+
26+
/**
27+
* @dev Restricts calls to functions using this modifier to only come from
28+
* delegatecalls.
29+
*/
30+
modifier onlyDelegated {
31+
require(address(this) != self, "ERC165Logic: undelegated calls disallowed");
32+
_;
33+
}
34+
35+
/**
36+
* @dev See {IERC165-supportsInterface}.
37+
*/
38+
function supportsInterface(bytes4 interfaceId) override(IERC165) public onlyDelegated virtual returns (bool) {
39+
ERC165State storage state = ERC165Storage._getState();
40+
return state._supportedInterfaces[interfaceId] || interfaceId == type(IERC165).interfaceId;
41+
}
42+
43+
/**
44+
* @dev Registers the contract as an implementer of the interface defined by
45+
* `interfaceId`. Support of the actual ERC165 interface is automatic and
46+
* registering its interface id is not required.
47+
*
48+
* See {IERC165Register-registerInterface}.
49+
*
50+
* Requirements:
51+
*
52+
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
53+
* - should only be callable by other extensions of the same contract
54+
*/
55+
function registerInterface(bytes4 interfaceId) override(IERC165Register) public onlyDelegated {
56+
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
57+
58+
ERC165State storage state = ERC165Storage._getState();
59+
state._supportedInterfaces[interfaceId] = true;
60+
}
61+
}

contracts/erc165/IERC165Logic.sol

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
/**
5+
* @dev Interface of the ERC165 standard, as defined in the
6+
* https://eips.ethereum.org/EIPS/eip-165[EIP].
7+
*
8+
* Implementers can declare support of contract interfaces, which can then be
9+
* queried by others ({ERC165Checker}).
10+
*
11+
* For an implementation, see {ERC165}.
12+
*/
13+
interface IERC165 {
14+
/**
15+
* @dev Returns true if this contract implements the interface defined by
16+
* `interfaceId`. See the corresponding
17+
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
18+
* to learn more about how these ids are created.
19+
*
20+
* This function call must use less than 30 000 gas.
21+
*/
22+
function supportsInterface(bytes4 interfaceId) external returns (bool);
23+
}
24+
25+
26+
/**
27+
* @dev Storage based implementation of the {IERC165} interface.
28+
*
29+
* Uses Extendable storage pattern to populate the registered interfaces storage variable.
30+
*/
31+
interface IERC165Register {
32+
/**
33+
* @dev Registers the contract as an implementer of the interface defined by
34+
* `interfaceId`. Support of the actual ERC165 interface is automatic and
35+
* registering its interface id is not required.
36+
*
37+
* Requirements:
38+
*
39+
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
40+
*/
41+
function registerInterface(bytes4 interfaceId) external;
42+
}

contracts/extendable/Extendable.sol

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ pragma solidity ^0.8.4;
44
import "../errors/Errors.sol";
55
import {CallerState, CallerContextStorage} from "../storage/CallerContextStorage.sol";
66
import {ExtendableState, ExtendableStorage} from "../storage/ExtendableStorage.sol";
7-
import {RoleState, Permissions} from "../storage/PermissionStorage.sol";
8-
import "../extensions/permissioning/PermissioningLogic.sol";
97
import "../extensions/extend/ExtendLogic.sol";
108

119
/**
@@ -74,9 +72,13 @@ contract Extendable {
7472
* logic and returns as such.
7573
*/
7674
function _delegate(address delegatee) internal virtual returns(bool) {
75+
_beforeFallback();
76+
7777
bytes memory out;
7878
(bool success, bytes memory result) = delegatee.delegatecall(msg.data);
7979

80+
_afterFallback();
81+
8082
// copy all returndata to `out` once instead of duplicating copy for each conditional branch
8183
assembly {
8284
returndatacopy(out, 0, returndatasize())
@@ -96,9 +98,6 @@ contract Extendable {
9698
}
9799
} else {
98100
// otherwise end execution and return the copied full returndata
99-
100-
// make sure to call _afterFallback before ending execution
101-
_afterFallback();
102101
assembly {
103102
return(out, returndatasize())
104103
}
@@ -111,32 +110,20 @@ contract Extendable {
111110
*
112111
* Initially attempts to locate an interfaceId match with a function selector
113112
* which are extensions that house single functions (singleton extensions)
114-
* If none is found then attempt execution by cycling through extensions and
115-
* calling.
116113
*
117114
* If no implementations are found that match the requested function signature,
118115
* returns ExtensionNotImplemented error
119116
*/
120117
function _fallback() internal virtual {
121-
_beforeFallback();
122-
ExtendableState storage state = ExtendableStorage._getStorage();
118+
ExtendableState storage state = ExtendableStorage._getState();
123119

124-
bool ok = false;
125120
// if an extension exists that matches in the functionsig
126121
if (state.extensionContracts[msg.sig] != address(0x0)) {
127122
// call it
128123
_delegate(state.extensionContracts[msg.sig]);
129124
} else {
130-
// else cycle through all extensions to find it if exists
131-
// this is not the preferred method for usage and only acts as a fallback
132-
for (uint i = 0; i < state.interfaceIds.length; i++) {
133-
ok = _delegate(state.extensionContracts[state.interfaceIds[i]]);
134-
if (ok) break; // exit after first successful execution
135-
}
125+
revert ExtensionNotImplemented();
136126
}
137-
138-
if (!ok) revert ExtensionNotImplemented(); // if there are no successful delegatecalls we assume no implementation.
139-
_afterFallback();
140127
}
141128

142129
/**
@@ -163,15 +150,15 @@ contract Extendable {
163150
* @dev Virtual hook that is called before _fallback().
164151
*/
165152
function _beforeFallback() internal virtual {
166-
CallerState storage state = CallerContextStorage._getStorage();
153+
CallerState storage state = CallerContextStorage._getState();
167154
state.callerStack.push(msg.sender);
168155
}
169156

170157
/**
171158
* @dev Virtual hook that is called after _fallback().
172159
*/
173160
function _afterFallback() internal virtual {
174-
CallerState storage state = CallerContextStorage._getStorage();
161+
CallerState storage state = CallerContextStorage._getState();
175162
state.callerStack.pop();
176163
}
177164
}

contracts/extensions/Extension.sol

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ pragma solidity ^0.8.4;
33

44
import "./IExtension.sol";
55
import "../errors/Errors.sol";
6-
import "@openzeppelin/contracts/utils/introspection/ERC165Storage.sol";
76
import "../utils/CallerContext.sol";
7+
import "../erc165/IERC165Logic.sol";
88

99
/**
1010
* ______ __ __ ______ ______ __ __ _____ ______ ______ __ ______
@@ -13,24 +13,61 @@ import "../utils/CallerContext.sol";
1313
* \ \_____\/\_\/\_\ \ \_\ \ \_____\ \_\\"\_\ \____-\ \_\ \_\ \_____\ \_____\ \_____\
1414
* \/_____/\/_/\/_/ \/_/ \/_____/\/_/ \/_/\/____/ \/_/\/_/\/_____/\/_____/\/_____/
1515
*
16-
* Base contract for all Extensions in the Extendable Framework
16+
* Base contract for Extensions in the Extendable Framework
1717
*
1818
* Inherit and implement this contract to create Extension contracts!
1919
*
2020
* Implements the EIP-165 standard for interface detection of implementations during runtime.
21+
* Uses the ERC165 singleton pattern where the actual implementation logic of the interface is
22+
* deployed in a separate contract. See ERC165Logic. Deterministic deployment guarantees the
23+
* ERC165Logic contract to always exist as static address 0x16C940672fA7820C36b2123E657029d982629070
2124
*
2225
* Define your custom Extension interface and implement it whilst inheriting this contract:
2326
* contract YourExtension is IYourExtension, Extension {...}
2427
*
2528
*/
26-
abstract contract Extension is ERC165Storage, CallerContext, IExtension {
29+
abstract contract Extension is CallerContext, IExtension, IERC165, IERC165Register {
30+
address constant ERC165LogicAddress = 0x16C940672fA7820C36b2123E657029d982629070;
31+
2732
/**
2833
* @dev Constructor registers your custom Extension interface under EIP-165:
2934
* https://eips.ethereum.org/EIPS/eip-165
3035
*/
3136
constructor() {
32-
_registerInterface(getInterfaceId());
33-
_registerInterface(type(IExtension).interfaceId);
37+
Interface[] memory interfaces = getInterface();
38+
for (uint256 i = 0; i < interfaces.length; i++) {
39+
Interface memory iface = interfaces[i];
40+
registerInterface(iface.interfaceId);
41+
42+
for (uint256 j = 0; j < iface.functions.length; j++) {
43+
registerInterface(iface.functions[j]);
44+
}
45+
}
46+
47+
registerInterface(type(IExtension).interfaceId);
48+
}
49+
50+
function supportsInterface(bytes4 interfaceId) external override virtual returns(bool) {
51+
(bool success, bytes memory result) = ERC165LogicAddress.delegatecall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceId));
52+
53+
if (!success) {
54+
assembly {
55+
revert(result, returndatasize())
56+
}
57+
}
58+
59+
return abi.decode(result, (bool));
60+
}
61+
62+
function registerInterface(bytes4 interfaceId) public override virtual {
63+
(bool success, ) = ERC165LogicAddress.delegatecall(abi.encodeWithSignature("registerInterface(bytes4)", interfaceId));
64+
65+
if (!success) {
66+
assembly {
67+
returndatacopy(0, 0, returndatasize())
68+
revert(0, returndatasize())
69+
}
70+
}
3471
}
3572

3673
/**
@@ -56,9 +93,9 @@ abstract contract Extension is ERC165Storage, CallerContext, IExtension {
5693
}
5794

5895
/**
59-
* @dev Virtual override declaration of getInterfaceId() function
96+
* @dev Virtual override declaration of getFunctionSelectors() function to silence compiler
6097
*
6198
* Must be implemented in inherited contract.
6299
*/
63-
function getInterfaceId() override public virtual pure returns(bytes4);
100+
function getInterface() override public virtual returns(Interface[] memory);
64101
}

contracts/extensions/IExtension.sol

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
//SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.4;
33

4+
struct Interface {
5+
bytes4 interfaceId;
6+
bytes4[] functions;
7+
}
8+
49
/**
510
* @dev Interface for Extension
611
*/
@@ -20,18 +25,25 @@ interface IExtension {
2025
* to be able to easily inspect and query for the current state of the interface and
2126
* integrate with it.
2227
*
23-
* See {ExtendLogic-getInterface} for an example.
28+
* See {ExtendLogic-getSolidityInterface} for an example.
2429
*/
25-
function getInterface() external pure returns(string memory);
30+
function getSolidityInterface() external pure returns(string memory);
2631

2732
/**
28-
* @dev Returns the interfaceId of current custom Extension interface
33+
* @dev Returns the interface IDs that are implemented by the Extension
34+
*
35+
* These are full interface IDs and ARE NOT function selectors. Full interface IDs are
36+
* XOR'd function selectors of an interface. For example the interface ID of the ERC721
37+
* interface is 0x80ac58cd determined by the XOR or all function selectors of the interface.
38+
*
39+
* If an interface only consists of a single function, then the interface ID is identical
40+
* to that function selector.
2941
*
3042
* Provides a simple abstraction from the developer for any custom Extension to
31-
* be EIP-165 compliant out-of-the-box simply by implementing this function.
43+
* be EIP-165 compliant out-of-the-box simply by implementing this function.
3244
*
3345
* Excludes any functions either already described by other interface definitions
3446
* that are not developed on top of this backbone i.e. EIP-165, IExtension
3547
*/
36-
function getInterfaceId() external pure returns(bytes4);
48+
function getInterface() external returns(Interface[] memory interfaces);
3749
}

contracts/extensions/InternalExtension.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import "../errors/Errors.sol";
1414
*
1515
* Base contract for Internal Extensions in the Extendable Framework
1616
*
17-
* Internal Extensions are used to house common functions that are used by other contract functions.
17+
* Internal Extensions are used to house common functions that are used by other contract extensions.
1818
* This is used to make internal functions usable across all your extensions without exposing it
1919
* to the external world.
2020
*

0 commit comments

Comments
 (0)