-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDiamond.sol
225 lines (203 loc) · 10.3 KB
/
Diamond.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
/******************************************************************************\
* Author: Nick Mudge
*
* Implementation of an example of a diamond.
/******************************************************************************/
import "./OwnershipFacet.sol";
import "./DiamondStorageContract.sol";
import "./DiamondHeaders.sol";
import "./DiamondFacet.sol";
import "./DiamondLoupeFacet.sol";
contract Diamond is IERC173Events, IERC165D, DiamondStorageContract, DiamondFacet {
constructor(address owner) public payable {
DiamondStorage storage ds = diamondStorage();
ds.contractOwner = owner;
emit OwnershipTransferred(address(0), owner);
// Create a DiamondFacet contract which implements the Diamond interface
DiamondFacet diamondFacet = new DiamondFacet();
// Create a DiamondLoupeFacet contract which implements the Diamond Loupe interface
DiamondLoupeFacet diamondLoupeFacet = new DiamondLoupeFacet();
// Create a OwnershipFacet contract which implements the ERC-173 Ownership interface
OwnershipFacet ownershipFacet = new OwnershipFacet();
bytes[] memory cut = new bytes[](4);
// Adding cut function
cut[0] = abi.encodePacked(
diamondFacet,
IDiamond.diamondCut.selector
);
// Adding diamond loupe functions
cut[1] = abi.encodePacked(
diamondLoupeFacet,
IDiamondLoupe.facetFunctionSelectors.selector,
IDiamondLoupe.facets.selector,
IDiamondLoupe.facetAddress.selector,
IDiamondLoupe.facetAddresses.selector
);
// Adding diamond ERC173 functions
cut[2] = abi.encodePacked(
ownershipFacet,
IERC173.transferOwnership.selector,
IERC173.owner.selector
);
// Adding supportsInterface function
cut[3] = abi.encodePacked(address(this), IERC165D.supportsInterfaceD.selector);
// execute non-standard internal diamondCut function to add functions
diamondCut(cut);
// adding ERC165 data
// ERC165
ds.supportedInterfaces[IERC165D.supportsInterfaceD.selector] = true;
// DiamondCut
ds.supportedInterfaces[IDiamond.diamondCut.selector] = true;
// DiamondLoupe
bytes4 interfaceID = IDiamondLoupe.facets.selector ^
IDiamondLoupe.facetFunctionSelectors.selector ^
IDiamondLoupe.facetAddresses.selector ^
IDiamondLoupe.facetAddress.selector;
ds.supportedInterfaces[interfaceID] = true;
// ERC173
ds.supportedInterfaces[IERC173.transferOwnership.selector ^
IERC173.owner.selector] = true;
}
// This is an immutable functions because it is defined directly in the diamond.
// Why is it here instead of in a facet? No reason, just to show an immutable function.
// This implements ERC-165.
function supportsInterfaceD(bytes4 _interfaceId) external override view returns (bool) {
DiamondStorage storage ds = diamondStorage();
return ds.supportedInterfaces[_interfaceId];
}
// Find facet for function that is called and execute the
// function if a facet is found and return any value.
fallback() external payable {
DiamondStorage storage ds;
bytes32 position = DiamondStorageContract.DIAMOND_STORAGE_POSITION;
assembly { ds_slot := position }
address facet = address(bytes20(ds.facets[msg.sig]));
require(facet != address(0));
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {revert(0, returndatasize())}
default {return (0, returndatasize())}
}
}
receive() external payable {}
// Non-standard internal function version of diamondCut
// This code is exaclty the same as externalCut in DiamondFacet, except it allows anyone to call it (internally) and is using
// 'bytes[] memory _diamondCut' instead of 'bytes[] calldata _diamondCut'
// The code is duplicated to prevent copying calldata to memory which
// causes an error for an array of bytes arrays.
function diamondCut(bytes[] memory _diamondCut) internal {
DiamondStorage storage ds = diamondStorage();
SlotInfo memory slot;
slot.originalSelectorSlotsLength = ds.selectorSlotsLength;
uint selectorSlotsLength = uint128(slot.originalSelectorSlotsLength);
uint selectorSlotLength = uint128(slot.originalSelectorSlotsLength >> 128);
if(selectorSlotLength > 0) {
slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
}
// loop through diamond cut
for(uint diamondCutIndex; diamondCutIndex < _diamondCut.length; diamondCutIndex++) {
bytes memory facetCut = _diamondCut[diamondCutIndex];
require(facetCut.length > 20, "Missing facet or selector info.");
bytes32 currentSlot;
assembly {
currentSlot := mload(add(facetCut,32))
}
bytes32 newFacet = bytes20(currentSlot);
uint numSelectors = (facetCut.length - 20) / 4;
uint position = 52;
// adding or replacing functions
if(newFacet != 0) {
// add and replace selectors
for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
bytes4 selector;
assembly {
selector := mload(add(facetCut,position))
}
position += 4;
bytes32 oldFacet = ds.facets[selector];
// add
if(oldFacet == 0) {
// update the last slot at then end of the function
slot.updateLastSlot = true;
ds.facets[selector] = newFacet | bytes32(selectorSlotLength) << 64 | bytes32(selectorSlotsLength);
// clear selector position in slot and add selector
slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorSlotLength * 32) | bytes32(selector) >> selectorSlotLength * 32;
selectorSlotLength++;
// if slot is full then write it to storage
if(selectorSlotLength == 8) {
ds.selectorSlots[selectorSlotsLength] = slot.selectorSlot;
slot.selectorSlot = 0;
selectorSlotLength = 0;
selectorSlotsLength++;
}
}
// replace
else {
require(bytes20(oldFacet) != bytes20(newFacet), "Function cut to same facet.");
// replace old facet address
ds.facets[selector] = oldFacet & CLEAR_ADDRESS_MASK | newFacet;
}
}
}
// remove functions
else {
slot.updateLastSlot = true;
for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
bytes4 selector;
assembly {
selector := mload(add(facetCut,position))
}
position += 4;
bytes32 oldFacet = ds.facets[selector];
require(oldFacet != 0, "Function doesn't exist. Can't remove.");
// Current slot is empty so get the slot before it
if(slot.selectorSlot == 0) {
selectorSlotsLength--;
slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
selectorSlotLength = 8;
}
slot.oldSelectorSlotsIndex = uint64(uint(oldFacet));
slot.oldSelectorSlotIndex = uint32(uint(oldFacet >> 64));
// gets the last selector in the slot
bytes4 lastSelector = bytes4(slot.selectorSlot << (selectorSlotLength-1) * 32);
if(slot.oldSelectorSlotsIndex != selectorSlotsLength) {
slot.oldSelectorSlot = ds.selectorSlots[slot.oldSelectorSlotsIndex];
// clears the selector we are deleting and puts the last selector in its place.
slot.oldSelectorSlot = slot.oldSelectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
// update storage with the modified slot
ds.selectorSlots[slot.oldSelectorSlotsIndex] = slot.oldSelectorSlot;
selectorSlotLength--;
}
else {
// clears the selector we are deleting and puts the last selector in its place.
slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
selectorSlotLength--;
}
if(selectorSlotLength == 0) {
delete ds.selectorSlots[selectorSlotsLength];
slot.selectorSlot = 0;
}
if(lastSelector != selector) {
// update last selector slot position info
ds.facets[lastSelector] = oldFacet & CLEAR_ADDRESS_MASK | bytes20(ds.facets[lastSelector]);
}
delete ds.facets[selector];
}
}
}
uint newSelectorSlotsLength = selectorSlotLength << 128 | selectorSlotsLength;
if(newSelectorSlotsLength != slot.originalSelectorSlotsLength) {
ds.selectorSlotsLength = newSelectorSlotsLength;
}
if(slot.updateLastSlot && selectorSlotLength > 0) {
ds.selectorSlots[selectorSlotsLength] = slot.selectorSlot;
}
emit DiamondCut(_diamondCut, address(0), new bytes(0));
}
}