запись в любой слот (write to any slot) - demonoved/A-trial-run-in-solidity GitHub Wiki
// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.20 and less than 0.9.0
pragma solidity ^0.8.23;
// хранилище solidity похож на массив длины 2^256, каждый слот хранит 32 байта
// порядок обьявление и тип переменных состояния определяют, какие слоты он будет использовать
// используя ассемблер можно записать в любой слот
library StorageSlot{
// обернем адресс в структуру, чтобы его можно было передавать в качестве указателя на хранилище
struct AddressSlot {
address value;
}
function getAddressSlot(bytes32 slot) internal pure returns(AddressSlot storage pointer){
assembly {
//получение указателя на AddressSlot, хранящегося в слоте
pointer.slot := slot
}
}
}
contract TestSlot {
bytes32 public constant TEST_SLOT = keccak256("TEST_SLOT");
function write(address _addr) external {
StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT);
data.value = _addr;
}
function get() external view returns(address){
StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT);
return data.value;
}
}
-
Лицензия и версия компилятора:
// SPDX-License-Identifier: MIT // compiler version must be greater than or equal to 0.8.20 and less than 0.9.0 pragma solidity ^0.8.23;
Эти строки указывают, что данный смарт-контракт распространяется под MIT лицензией, и для его компиляции нужен компилятор Solidity версии не ниже 0.8.23 и меньше 0.9.0.
-
Комментарий о хранилище в Solidity:
Следующий комментарий описывает, как работает хранилище в Solidity:
"хранилище solidity похож на массив длины 2^256, каждый слот хранит 32 байта"
-
Библиотека
StorageSlot
:В коде объявляется библиотека
StorageSlot
, которая предоставляет утилиту для взаимодействия со слотами хранилища напрямую через ассемблер.library StorageSlot { struct AddressSlot { address value; } function getAddressSlot(bytes32 slot) internal pure returns(AddressSlot storage pointer) { assembly { pointer.slot := slot } } }
Эта библиотека позволяет вам получать и устанавливать значения в произвольный слот хранилища. Функция
getAddressSlot
принимает ключ слота (bytes32
) и возвращает ссылку наAddressSlot
, который является указателем на слот хранилища. -
Контракт
TestSlot
:contract TestSlot { bytes32 public constant TEST_SLOT = keccak256("TEST_SLOT"); ... }
Здесь объявляется контракт
TestSlot
, который использует библиотекуStorageSlot
для записи и чтения адресов в заданный слот хранилища. Хешkeccak256("TEST_SLOT")
используется для генерации уникального идентификатора слота. -
Функции контракта:
Функция
write
принимает адрес и записывает его в хранилище:function write(address _addr) external { StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT); data.value = _addr; }
А функция
get
возвращает значение из хранилища:function get() external view returns(address) { StorageSlot.AddressSlot storage data = StorageSlot.getAddressSlot(TEST_SLOT); return data.value; }
В итоге этот код показывает, как можно взаимодействовать с низкоуровневыми деталями хранилища смарт-контрактов, в частности, напрямую с слота хранилища по заданному ключу. Это можно использовать для эффективной организации хранения данных в контракте, особенно когда требуется гибкая или оптимизированная схема хранения.