ethereum_smartcontract_intro

Форк
0
311 строк · 13.0 Кб
1
// SPDX-License-Identifier: UNLICENSED
2

3
// Ограничение на версию компилятора
4
pragma solidity >=0.8;
5

6
// Делаем видимыми в текущем файле конкретные сущности из другого файла.
7
import {DebugPrintContract, DebugStorageContract} from "./Debug.sol";
8

9
// Библиотека для отладочного вывода в тестовой сети.
10
import "hardhat/console.sol";
11

12
// Константа времени компиляции.
13
string constant DEMO_CONTRACT_NAME = "DemoContract";
14

15

16
// Библиотека - это контракт без состояния (все вызовы идут через delegatecall),
17
// для которого предоставляется упрощённый синтаксис вызова из других контрактов.
18
// Remix может автоматически деплоить контракт библиотеки (отдельной транзакцией)
19
// и подставлять адрес этого контракта в места вызова функций библиотеки.
20
// Если библиотека уже задеплоина, то можно указать Remix'у её адрес.
21
library DemoLibrary {
22

23
    function DLadd (uint a, uint b) public pure returns (uint) {
24
        return a + b;
25
    }
26
}
27

28

29
// Будет использоваться в качестве родительского контракта.
30
// Но может быть и задеплоен сам, т.к. не является абстрактным.
31
contract BaseContract {
32

33
    // Для существующего типа можно завести синоним.
34
    type ValueType is uint;
35

36
    // константу (const) надо инициализировать во время компиляции
37
    //uint constant baseVar;
38
    // неизменяемую переменную можно инициализировать в конструкторе контракта
39
    ValueType internal immutable _value;
40

41
    constructor (uint value) {
42

43
        // аналог asssert в других языках - используется для проверки инвариантов
44
        assert (address(this).balance == 0);
45

46
        // конвертация из значения исходного типа
47
        _value = ValueType.wrap(value);
48
    }
49

50
    // Возможна перегрузка функций по аргументам.
51
    function f () public pure {
52

53
    }
54

55
    function f (uint a) public pure {
56

57
    }
58

59
    // Можно определять собственные модификаторы функций.
60
    // Похожи на декораторы в Python.
61
    modifier NotNull (uint a) {
62

63
        // Проверка условия.
64
        // Если условие ложно, выполняется инструкция revert с указанной строкой.
65
        require(a != 0, "is null");
66
        
67
        _;  // вызов исходной функции
68
    }
69
}
70

71

72
// Контракт наследуется от двух контрактов.
73
contract DemoContract is BaseContract, DebugPrintContract {
74

75
    // В этом контракте для типа uint будут доступны функции библиотеки
76
    // с первым аргументом этого типа.
77
    using DemoLibrary for uint;
78

79
    using DebugStorageContract for *;
80

81
    // Константа должна быть проинициализирована во время компиляции.
82
    string constant contractName = DEMO_CONTRACT_NAME;
83

84
    // целочисленные беззнаковые переменные
85
    uint8 private ui8 = 0x8;
86
    uint16 private ui16 = 0x16;
87
    uint24 private ui24 = 0x24;
88
    // ...
89
    uint256 private ui256 = 0x256;
90

91
    // целочисленные знаковые переменные
92
    int8 private i8 = 0x18;
93
    //...
94
    int256 private i256 = 0x1256;
95

96
    // массивы байт фиксированной длины
97
    bytes1 private b1 = 0x01;
98
    bytes2 private b2 = 0x0202;
99
    bytes3 private b3 = 0x030303;
100
    bytes4 private b4 = 0x04040404;
101
    //...
102
    bytes32 private b32 = hex"0505050505050505050505050505050505050505050505050505050505050505";
103

104
    // массив фиксированной длины
105
    uint64[2] private uia2 = [0x010101010101, 0x02020202];
106

107
    // массив произвольной длины
108
    // В его слоте располагается текущая длина массива.
109
    // i-ый элемент располагается в слоте (keccak256(uida.slot) + i * elementSize)
110
    uint[] private uida;
111

112
    // массив байт произвольной длины
113
    bytes ba = hex"0102030405";
114

115
    // Для отображения отводится пустой слот.
116
    // Элемент с ключом key располагается в слоте keccak256(key, uumap.slot)
117
    mapping (uint => uint) uumap;
118

119
    struct Entry {
120
        uint256 a;
121
        uint8 b;
122
    }
123

124
    Entry[5] sea5;
125

126
    // Конструктор может быть только один.
127
    // Можно передавать аргументы в конструкторы базовых классов.
128
    constructor (uint a) payable BaseContract(a) {
129

130
        DebugPrint ("a = ", a);
131
        DebugPrint ("msg.gas = ", gasleft());
132

133
        DebugPrint ("1 + 2 = ", DemoLibrary.DLadd (1, 2));
134

135
        PrintInfo();
136
    }
137

138
    // Переопределяем виртуальную функцию из базового класса.
139
    function GetContractName () internal override pure returns (string memory) {
140
        
141
        return contractName;
142
    }
143

144
    function PureFunction (uint a) pure public NotNull(a) returns (uint) {
145
        console.log ("a = %d\n", a);
146
        return a + 1;
147
    }
148

149
    function TestBytes (bytes calldata a) public returns (bytes calldata) {
150

151
        //uint slot = DebugStorageContract.GetStorageSlot (b1);
152
        PrintBytes (a);
153

154
        PrintInfo();  
155
        return a;
156
    }
157

158
    function TestVariables () public {
159

160
        uint slot;
161
        uint offset;
162
        //assembly {slot := contractName.slot}
163
        //DebugPrint ("contractName slot: ", slot);
164

165
        // Демонстрация размещения статических переменных в storage
166

167
        assembly {  slot := ui8.slot        // адрес 256-битного слота в storage
168
                    offset := ui8.offset}   // смещение в байтах внутри слота
169
        DebugPrint ("ui8 slot, offset: ", slot, offset);
170

171
        assembly {  slot := ui16.slot
172
                    offset := ui16.offset}
173
        DebugPrint ("ui16 slot, offset: ", slot, offset);
174

175
        assembly {  slot := ui24.slot
176
                    offset := ui24.offset}
177
        DebugPrint ("ui24 slot, offset: ", slot, offset);
178

179
        assembly {  slot := ui256.slot
180
                    offset := ui256.offset}
181
        DebugPrint ("ui256 slot, offset: ", slot, offset);
182

183
        assembly {  slot := i8.slot
184
                    offset := i8.offset}
185
        DebugPrint ("i8 slot, offset: ", slot, offset);
186

187
        assembly {  slot := i256.slot
188
                    offset := i256.offset}
189
        DebugPrint ("i256 slot, offset: ", slot, offset);
190

191
        assembly {  slot := b1.slot
192
                    offset := b1.offset}
193
        DebugPrint ("b1 slot, offset: ", slot, offset);
194

195
        assembly {  slot := b2.slot
196
                    offset := b2.offset}
197
        DebugPrint ("b2 slot, offset: ", slot, offset);
198
        
199
        assembly {  slot := b3.slot
200
                    offset := b3.offset}
201
        DebugPrint ("b3 slot, offset: ", slot, offset);
202

203
        assembly {  slot := b4.slot
204
                    offset := b4.offset}
205
        DebugPrint ("b5 slot, offset: ", slot, offset);
206

207
        assembly {  slot := b32.slot
208
                    offset := b32.offset}
209
        DebugPrint ("b32 slot, offset: ", slot, offset);
210

211
        assembly {  slot := uia2.slot
212
                    offset := uia2.offset}
213
        DebugPrint ("uia2 slot, offset: ", slot, offset);
214

215
        assembly {  slot := uida.slot
216
                    offset := uida.offset}
217
        DebugPrint ("uida slot, offset: ", slot, offset);
218

219
        assembly {  slot := ba.slot
220
                    offset := ba.offset}
221
        DebugPrint ("ba slot, offset: ", slot, offset);
222

223
        assembly {  slot := uumap.slot
224
                    offset := uumap.offset}
225
        DebugPrint ("uumap slot, offset: ", slot, offset);
226

227
        assembly {  slot := sea5.slot
228
                    offset := sea5.offset}
229
        DebugPrint ("sea5 slot, offset: ", slot, offset);
230

231
        DebugStorageContract.PrintStorage (0, 12);
232

233

234
        // Демонстрация размещения динамических переменных в storage
235

236
        uint uidaSlot;
237
        assembly { uidaSlot := uida.slot }
238

239
        // в слоте массива сейчас нулевая длина
240
        DebugPrint ("uida slot: ", uidaSlot, DebugStorageContract.ReadStorageValue (uidaSlot));
241

242
        // помещаем два элемента
243
        uida.push (1111111111111111111111111111111111111111111112222);
244
        uida.push (2222222222222222222222222222222222222222222223333);
245
        
246
        // в слоте массива сейчас 2
247
        DebugPrint ("uida slot: ", uidaSlot, DebugStorageContract.ReadStorageValue (uidaSlot));
248

249
        for (uint i = 0; i < uida.length; ++i) {
250
            // высчитываем адрес слота для i-го элемента
251
            uint elementSlot = uint256(keccak256(abi.encodePacked(uidaSlot))) + i * 1;
252
            // выводим значение по адресу
253
            DebugPrint ("uida element slot ", elementSlot, DebugStorageContract.ReadStorageValue (elementSlot));
254
        }
255

256

257
        uint uumapSlot;
258
        assembly { uumapSlot := uumap.slot }
259

260
        // в слоте отображения всегда 0
261
        DebugPrint ("uumap slot: ", uumapSlot, DebugStorageContract.ReadStorageValue (uumapSlot));
262

263
        // помещаем два элемента
264
        uumap[0] = 3333333333333333333333333333333333333333333334444;
265
        uumap[1] = 4444444444444444444444444444444444444444444445555;
266

267
        // высчитываем адрес слота для uumap[0]
268
        uint element0Slot = uint256(keccak256(abi.encodePacked(uint(0), uumapSlot)));
269
        // выводим значение по адресу
270
        DebugPrint ("uumap[0] element slot ", element0Slot, DebugStorageContract.ReadStorageValue (element0Slot));
271

272
        // высчитываем адрес слота для uumap[1]
273
        uint element1Slot = uint256(keccak256(abi.encodePacked(uint(1), uumapSlot)));
274
        // выводим значение по адресу
275
        DebugPrint ("uumap[1] element slot ", element1Slot, DebugStorageContract.ReadStorageValue (element1Slot));
276

277

278
        // Перепишем элементом динамического массива uida элемент отображения uumap.
279
        // Адреса их слотов мы только что подсчитали.
280
        // Слоты элементов динамического массива идут последовательно, начиная с некоторого.
281
        // Поэтому надо просто вычислить индекс массива, который будет попадать
282
        // в нужный элемент отображения uumap.
283

284
        // Переписываем поле длины массива максимальным значением
285
        DebugStorageContract.WriteStorageValue (uidaSlot, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
286
        // адрес слота 0-го элемента массива
287
        uint uidaElement0Slot = uint256(keccak256(abi.encodePacked(uidaSlot)));
288
        
289
        // высчитываем индекс элемента массива, который попадает в тот же слот, что и uumap[1]
290
        uint writeIndex;
291
        unchecked {
292
            writeIndex = element1Slot - uidaElement0Slot;
293
        }
294

295
        DebugPrint ("writeIndex: ", writeIndex);
296

297
        // выводим элемент до перезаписи
298
        DebugPrint ("uumap[1]: ", uumap[1]);
299
        // перезаписываем
300
        uida[writeIndex] = 5555555555555555555555555555555555555555555556666;
301
        // выводим тот же элемент после перезаписи
302
        DebugPrint ("uumap[1]: ", uumap[1]);
303

304
        // Можно заметить, что mapping также позволяет писать по произвольным адресам
305
        // в storage, к тому же в нём нет ограничений наподобие длины массива.
306
        // Но если мы хотим переписать элемент в storage по фиксированному адресу,
307
        // то вычислительно невозможно подобрать ключ, который отобразиться на этот элемент.
308

309
    }
310

311
}
312

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.