Solidity е език за имплементиране на Smart Contracts.
Няма float
, съответно integer операциите truncate-ват integers
Транзакция vs. call - транзакцията се приема като множество от действия, които променят ledger-a, съотв. има и такси, изразходва се газ, докато при call се извършва само четене.
В Solidity можем да overload-ваме само нормалните функции, но не и конструктора
Конструкторът е единствен
рядко използваме низове и не можем да сравняваме низове
mappings, т.е. хеш-таблиците нямат size
или length
трябва да внимаваме с циклите, т.к. потенциално може да изхабим повече газ, отколкото очакваме
3 вида памет - storage, memory, calldata/callstack
❗❗❗ Когато пишем smart contracts, които комуникират с други, комуникацията се случва със серия от байтове
ограничаване на размера на елементите; overflow се handle-ва, т.е. имплицитно
public
, private
- както от ООП
internal
- както protected от ООП
external
- може да се достъпва само от външен smart contract, има значение от страна на бизнес логиката, напр. ако имаме конкретна функция и няма логика да има действие без други smart contracts
view
- компилаторът ни позволява само да четем от състоянието, т.е. callpure
- нито чете от ledger-a, нито модифицира ledger-a, т.е. прилича на статична функция, но не е точноpayable
- тези функции, които могат да получават крипто
constant
Ако изпуснем модификатор, се приема by default модификаторът с най-висок приоритет
Инструментариум, с който ограничаваме поведението на функцията, за да можем да осигурим по-високо ниво на точност на изпълнението на бизнес логиката
require(<boolean expression>, "string message to display if expression is false")
Ако булевият израз се оцени до лъжа, то се връща газта до момента и се дава причината.
Обикновено се поставят в началото на функциите с цел потенциално спестяване на газ.
modifier isHigher5(uint256 _val) {
require(_val > 5, "Number must be higher than 5");
_; // placeholder, взема се кода на функцията и се продължава
}
function setValue(uint256 _val) public isHigher5(_val) {
num = _val;
}
//SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract HelloContract {
// uint256 private num = 0; // можем да укажем модификатор за достъп на полетата
uint256 num = 0;
constructor() {
}
modifier isHigher5(uint256 _val) {
require(_val > 5, "Number must be higher than 5");
_;
}
function setValue(uint256 _val) public isHigher5(_val) {
num = _val;
}
// модификатор за достъп, модификатор за компилатор, модификатор за requirements, тип върнатата стойност
function getValue() public view returns(uint256, uint256) {
// по дефиниция само четат
return (num, num + 1);
}
function divTest(uint v1, uint v2) public pure returns(uint256) {
return v1 / v2;
}
}
contract A {
uint256 internal num = 10;
}
contract B is A {
function getNum() public view returns(uint256) {
return num;
}
}
contract C is A, B {
}
storage - всичко, което е маркирано със storage казва, че промеливите отиват директно в ledger-a, state fields са storage variables
memory - живее само по време на изпълнение на smart contract-а, динамична памет, която заделяме
calldata/stack - сходно на memory, разликата е, че е immutable, оптимизира първите 2, особено когато smart contract-а се пуска отвън, нещо като const reference параметър на функция; изобщо се позволява само като параметър на функция
Всички state fields by default са storage.
Всичко извън state fields по конвенция е memory или calldata/stack
//SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract NewContract {
uint32[] private array;
function setArray(uint32[] calldata arr) public {
delete array;
require(array.length == 0, "This ain't working");
for(uint256 i = 0; i < arr.length; ++i) {
array.push(arr[i]);
}
}
function getArray() public view returns(uint32[] memory) {
return array;
}
function setLocalArray(uint256 _size) public pure returns(uint256[] memory) {
uint256[] memory arr = new uint256[](_size);
// заделяме памет
// ако не използваме new, arr има стойност подобна на null
for(uint256 i = 0; i < _size; ++i) {
arr[i] = i;
// няма .push(i) метод, масивът не се преоразмерява
}
return arr;
}
}
function comparestr(string calldata str1,
string calldata str2) public pure returns(bool) {
return keccak256(abi.encode(str1)) == keccak256(abi.encode(str2));
}
function comparearr() public pure returns(bool) {
uint8[5] memory arr1 = [1, 2, 3, 4, 5];
uint8[5] memory arr2 = [1, 2, 3, 4, 5];
//arr2[2] = 7;
return keccak256(abi.encode(arr1)) == keccak256(abi.encode(arr2));
}
abi.encode
vs abi.encodePacked
encode
добавя padding, докато encodePacked
не, по-сигурен вариант при работа с низове е encode
.mapping променливите са само storage
size
или length
атрибути
contract NewContract {
uint32[] private array;
mapping(uint32 => address) mapp;
uint32[] keys;
...