forked from wighawag/hardhat-deploy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDiamondFacet.sol
143 lines (137 loc) · 7.38 KB
/
DiamondFacet.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.4;
pragma experimental ABIEncoderV2;
/******************************************************************************\
* Author: Nick Mudge
* from https://github.com/mudgen/Diamond/blob/ca15562a2858a4a4696526b1f6b18a4adef10617/contracts/
*
* Implementation of Diamond facet.
* This is gas optimized by reducing storage reads and storage writes.
* This code is as complex as it is to reduce gas costs.
/******************************************************************************/
import "./DiamondStorageContract.sol";
import "./DiamondHeaders.sol";
contract DiamondFacet is Diamond, DiamondStorageContract {
bytes32 constant CLEAR_ADDRESS_MASK = 0x0000000000000000000000000000000000000000ffffffffffffffffffffffff;
bytes32 constant CLEAR_SELECTOR_MASK = 0xffffffff00000000000000000000000000000000000000000000000000000000;
// This struct is used to prevent getting the error "CompilerError: Stack too deep, try removing local variables."
// See this article: https://medium.com/1milliondevs/compilererror-stack-too-deep-try-removing-local-variables-solved-a6bcecc16231
struct SlotInfo {
uint originalSelectorSlotsLength;
bytes32 selectorSlot;
uint oldSelectorSlotsIndex;
uint oldSelectorSlotIndex;
bytes32 oldSelectorSlot;
bool updateLastSlot;
}
function diamondCut(bytes[] memory _diamondCut) public override {
DiamondStorage storage ds = diamondStorage();
require(msg.sender == ds.contractOwner, "Must own the contract.");
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);
}
}