-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSimpleProxy.sol
120 lines (103 loc) · 5.49 KB
/
SimpleProxy.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/// @notice Контракт с логикой реализации
contract Logic {
address public initAddress;
uint256 private _value;
/**
* @notice Используется для установки данных на контракт при инициализации
* @dev По сути заменитель constructor()
*/
function initialize(address _initAddress) public {
initAddress = _initAddress;
}
/**
* @notice Позволяет записать значение в state
* @param _newValue Новое значение для записи в state
*/
function store(uint256 _newValue) public {
_value = _newValue;
}
/**
* @notice Позволяет получить значение из state
* @return _value Значение из state
*/
function retrieve() public view returns (uint256) {
return _value;
}
}
/**
* @notice Контракт прокси
* @dev Не имеет реализации. Будет делегировать вызов контракту логики.
* Фактическое хранение данных будет на контракте прокси.
* Взаимодействие с контрактом логики только через вызовы на прокси контракте
*/
contract Proxy {
struct AddressSlot {
address value;
}
/**
* @notice Внутренняя переменная для определения места записи информации об адресе контракта логики
* @dev Согласно EIP-1967 слот можно рассчитать как bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
* Выбираем псевдослучайный слот и записывает адрес контракта логики в этот слот. Эта позиция слота должна быть достаточно случайной,
* чтобы переменная в контракте логики никогда не занимала этот слот.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
constructor(address logic) {
_setImplementation(logic);
}
/// @notice Возвращает адрес установленного контракта логики для контракта прокси
function getImplementation() external view returns (address) {
return _getImplementation();
}
/// @notice Устанавливает адрес контракта логики для контракта прокси
function setImplementation(address _newLogic) external {
_setImplementation(_newLogic);
}
function _delegate(address _implementation) internal {
// Необходима assembly вставка, потому что невозможно получить доступ к слоту для возврата значения в обычном solidity
assembly {
// Копируем msg.data и получаем полный контроль над памятью для этого вызова.
calldatacopy(0, 0, calldatasize())
// Вызываем контракт реализации
let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)
// Копируем возвращаемые данные
returndatacopy(0, 0, returndatasize())
switch result
// Делаем revert, если возвращенные данные равны нулю.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @notice Возращает адрес установленного контракта логики для контракта прокси
* @dev Адрес логики хранится в специально отведенном слоте, для того, чтобы невозможно было случайно затереть значение
*/
function _getImplementation() internal view returns (address) {
return getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @notice Устанавливает адрес контракта логики для котракта прокси
* @dev Адрес логики хранится в специально отведенном слоте, для того, чтобы невозможно было случайно затереть значение
*/
function _setImplementation(address newImplementation) private {
getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @notice Возвращает произвольный слот памяти типа storage
* @param slot Указатель на слот памяти storage
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/// @dev Любые вызовы функций контракта логики через прокси будут делегироваться благодаря обработке внутри fallback
fallback() external {
_delegate(_getImplementation());
}
}