Оптимизация газа - 2
Если взглянуть на таблицу стоимости газа, то мы заметим, что вызов переменной в первый раз стоит нам 2100 газа, а во второй - уже 100 газа. Эта разница может стать проблемой, особенно, когда мы работаем с динамическими массивами в циклах. Посмотрите на второй пример на скрине.
Работая с данными внутри функции стоит намного меньше газа, даже с учетом того, что добавились новые строчки кода. В данном примере мы экономим почти 2000 газа!
#gas #optimization #hint
Если взглянуть на таблицу стоимости газа, то мы заметим, что вызов переменной в первый раз стоит нам 2100 газа, а во второй - уже 100 газа. Эта разница может стать проблемой, особенно, когда мы работаем с динамическими массивами в циклах. Посмотрите на второй пример на скрине.
Работая с данными внутри функции стоит намного меньше газа, даже с учетом того, что добавились новые строчки кода. В данном примере мы экономим почти 2000 газа!
#gas #optimization #hint
🔥2
Оптимизация газа - 3
Изменение значения с "0" до любого-другого в сети Эфира стоит 20 000 газа (Gsset), в то время как обнуление значения может возвращает часть газа на баланс (Rsclear). Тут важно отметить, что вернуть можно только 20% от стоимости транзакции, которая превышает 24 000 газа.
Пример 1
У Алисы 10 токенов, а у Боба 0 токенов. Алиса пересылает 5 токенов Бобу. Таким образом баланс Алисы меняется с 10 токенов до 5, а у Боба с 0 до 5. Итого:
У Алисы - 5000 газа + у Боба - 20 000 газа. Всего 25 000 газа за транзакцию.
Пример 2
У Алисы 10 токенов, у Боба - 0. Алиса пересылает все 10 токенов Бобу, и ее баланс обнуляется. Получается:
У Алисы - 5000 газа + у Боба 20 000 газа = 25 000 газа. При этом Алисе вернут 4 800 газа обратно. Итого транзакция у нас выйдет в 20 200 газа.
Очевидно, что второй пример позволяет нам сэкономить немного газа, который может быть потрачен на другие операции в контракте.
#gas #optimization #hint
Изменение значения с "0" до любого-другого в сети Эфира стоит 20 000 газа (Gsset), в то время как обнуление значения может возвращает часть газа на баланс (Rsclear). Тут важно отметить, что вернуть можно только 20% от стоимости транзакции, которая превышает 24 000 газа.
Пример 1
У Алисы 10 токенов, а у Боба 0 токенов. Алиса пересылает 5 токенов Бобу. Таким образом баланс Алисы меняется с 10 токенов до 5, а у Боба с 0 до 5. Итого:
У Алисы - 5000 газа + у Боба - 20 000 газа. Всего 25 000 газа за транзакцию.
Пример 2
У Алисы 10 токенов, у Боба - 0. Алиса пересылает все 10 токенов Бобу, и ее баланс обнуляется. Получается:
У Алисы - 5000 газа + у Боба 20 000 газа = 25 000 газа. При этом Алисе вернут 4 800 газа обратно. Итого транзакция у нас выйдет в 20 200 газа.
Очевидно, что второй пример позволяет нам сэкономить немного газа, который может быть потрачен на другие операции в контракте.
#gas #optimization #hint
Оптимизация газа - 4
Хранение данных в calldata всегда стоит меньше газа, чем хранение в memory. Это с учетом того, что вы будете просто считывать данные в calldata, а не изменять их. Во втором случае, memory более разумный выбор.
#gas #optimization #hint
Хранение данных в calldata всегда стоит меньше газа, чем хранение в memory. Это с учетом того, что вы будете просто считывать данные в calldata, а не изменять их. Во втором случае, memory более разумный выбор.
#gas #optimization #hint
Оптимизация газа - 5
В Solidity существует 4 способа инкремента / декремента числа на 1.
Для каждой задачи используется разный опкод, потому стоимость газа будет слегка отличаться.
#gas #optimization #hint
В Solidity существует 4 способа инкремента / декремента числа на 1.
Для каждой задачи используется разный опкод, потому стоимость газа будет слегка отличаться.
#gas #optimization #hint
👍1
Оптимизация газа - 6
Solidity Optimizer прорабатывает две вещи в смарт контрактах: стоимость деплоя и стоимость вызова функций. Чем меньше "runs" установлено в Оптимизатор, чем меньше будет стоимость деплоя. С другой стороны, чем больше "runs" - тем меньше стоимость вызова функций.
Вы можете сами настраивать количество "runs", чтобы оптимизиваровать стоимость газа конкретно для вашего контракта.
#gas #optimization #hint
Solidity Optimizer прорабатывает две вещи в смарт контрактах: стоимость деплоя и стоимость вызова функций. Чем меньше "runs" установлено в Оптимизатор, чем меньше будет стоимость деплоя. С другой стороны, чем больше "runs" - тем меньше стоимость вызова функций.
Вы можете сами настраивать количество "runs", чтобы оптимизиваровать стоимость газа конкретно для вашего контракта.
#gas #optimization #hint
Оптимизация газа - 7
Функции помеченные как payable потребляют меньше газа, так как для них требуется меньше опкода для проверки, может ли другой контракт пересылать Эфир.
#gas #optimization #hint
Функции помеченные как payable потребляют меньше газа, так как для них требуется меньше опкода для проверки, может ли другой контракт пересылать Эфир.
#gas #optimization #hint
Оптимизация газа - 8
Когда вызову в контракте требуется больше 32kb памяти в одной транзакции, стоимость газа в разы возрастает. Посмотрите на пример.
Стоимость добавление 10 000 uint256 в память больше примерно в 10 раз больше добавления 1000 значений.
При этом стоимость добавления 20 000 значений уже больше в 4 раза стоимости добавления 10 000 uint256.
Чтобы избежать этого, вам стоит разбивать большие данные на более мелкие части и работать прицельно с ними.
#gas #optimization #hint
Когда вызову в контракте требуется больше 32kb памяти в одной транзакции, стоимость газа в разы возрастает. Посмотрите на пример.
Стоимость добавление 10 000 uint256 в память больше примерно в 10 раз больше добавления 1000 значений.
При этом стоимость добавления 20 000 значений уже больше в 4 раза стоимости добавления 10 000 uint256.
Чтобы избежать этого, вам стоит разбивать большие данные на более мелкие части и работать прицельно с ними.
#gas #optimization #hint
Оптимизация газа - 9
В выражениях, где используются операторы сравнения (<,>,<=,>=), дешевле будет использовать простые операторы - < или >, так как в случае с <= и >= компилятор сначала использует опкод "больше / меньше", а после опкод "iszero", чтобы проверить результат работы предыдущего сравнения.
#gas #optimization #hint
В выражениях, где используются операторы сравнения (<,>,<=,>=), дешевле будет использовать простые операторы - < или >, так как в случае с <= и >= компилятор сначала использует опкод "больше / меньше", а после опкод "iszero", чтобы проверить результат работы предыдущего сравнения.
#gas #optimization #hint
😱1
Оптимизация газа - 10
Когда вы используете require с двумя и более проверками, то в начало ставьте операторы && или | | для уменьшения стоимости газа. Например:
- require(A | | B) - если true, то компилятор не будет проверять остальные значения;
- require (A && B) - если false, то компилятор также остановит проверку дальше;
#gas #optimization #hint
Когда вы используете require с двумя и более проверками, то в начало ставьте операторы && или | | для уменьшения стоимости газа. Например:
- require(A | | B) - если true, то компилятор не будет проверять остальные значения;
- require (A && B) - если false, то компилятор также остановит проверку дальше;
#gas #optimization #hint
Оптимизация газа - 11
Указание правильной видимости функций влияет не только на безопасность ее исполнения, но и на экономию газа.
Например, создав external функцию, вы установите место хранения ее параметров, как calldata. Это позволит экономить газ каждый раз при ее вызове.
#gas #optimization #hint
Указание правильной видимости функций влияет не только на безопасность ее исполнения, но и на экономию газа.
Например, создав external функцию, вы установите место хранения ее параметров, как calldata. Это позволит экономить газ каждый раз при ее вызове.
#gas #optimization #hint
👍1
Оптимизация газа - 12
В Solidity некоторые data types дороже остальных. Вот несколько рекомендаций к их использованию:
- Тип uint лучше использовать вместо string, если это возможно;
- uint256 стоит меньше, чем uint8;
- bytes лучше использовать вместо byte[];
- Если длина bytes может быть ограничена, то лучше использовать наименьшие числа от bytes1 до bytes32;
- bytes32 дешевле, чем string;
#gas #optimization #hint
В Solidity некоторые data types дороже остальных. Вот несколько рекомендаций к их использованию:
- Тип uint лучше использовать вместо string, если это возможно;
- uint256 стоит меньше, чем uint8;
- bytes лучше использовать вместо byte[];
- Если длина bytes может быть ограничена, то лучше использовать наименьшие числа от bytes1 до bytes32;
- bytes32 дешевле, чем string;
#gas #optimization #hint
👍1
Оптимизация газа - 13
Если вам нужно провести цикл по массиву, то, для экономии газа, следует зафиксировать его длину в переменную выше. Например так:
uint length = arr.length;
for (uint i = 0; i < length; i++) {
// do something that doesn't change arr.length
}
так как в случае i < arr.length компилятор будет считывать длину при каждой итерации, что потребует дополнительного газа.
#gas #optimization #hint
Если вам нужно провести цикл по массиву, то, для экономии газа, следует зафиксировать его длину в переменную выше. Например так:
uint length = arr.length;
for (uint i = 0; i < length; i++) {
// do something that doesn't change arr.length
}
так как в случае i < arr.length компилятор будет считывать длину при каждой итерации, что потребует дополнительного газа.
#gas #optimization #hint
👍1
Оптимизация газа - 14
В некоторых случаях, пометив переменные как immutable, можно сэкономить немного газа при их вызове позже. Например:
contract C {
/// The owner is set during construction time, and never changed afterwards.
address public owner = msg.sender;
}
В этом случае, при вызове owner будет задействован sload, и будет затрачено 2100 газа при первом вызове и 100 - при последующих. Однако в следующем примере:
contract C {
/// The owner is set during construction time, and never changed afterwards.
address public immutable owner = msg.sender;
}
sload не применяется и вызов owner будет стоить всего 3 газа.
#gas #optimization #hint
В некоторых случаях, пометив переменные как immutable, можно сэкономить немного газа при их вызове позже. Например:
contract C {
/// The owner is set during construction time, and never changed afterwards.
address public owner = msg.sender;
}
В этом случае, при вызове owner будет задействован sload, и будет затрачено 2100 газа при первом вызове и 100 - при последующих. Однако в следующем примере:
contract C {
/// The owner is set during construction time, and never changed afterwards.
address public immutable owner = msg.sender;
}
sload не применяется и вызов owner будет стоить всего 3 газа.
#gas #optimization #hint
👍1
Оптимизация газа - 15
Используйте смещения вместо деления. Более того в этом случае не произойдет overflow.
#gas #optimization #hint
Используйте смещения вместо деления. Более того в этом случае не произойдет overflow.
#gas #optimization #hint
Оптимизация газа - 16
Тип bool в Solidity занимает 1 байт в памяти, из которых используется только один байт. При необходимости нескольких булевых значений можно заменить bool на uint32 или uint256 и битовой арифметики. Таким образом, uint256 может хранить до 256 булевых значений.
#gas #optimization #hint
Тип bool в Solidity занимает 1 байт в памяти, из которых используется только один байт. При необходимости нескольких булевых значений можно заменить bool на uint32 или uint256 и битовой арифметики. Таким образом, uint256 может хранить до 256 булевых значений.
#gas #optimization #hint
👍1
Оптимизация газа - 17
Ключевое слово event позволяет объявить события которая потом можно пробрасывать во время выполнения контракта, и эти события будут доступны извне. Помечание аргументов ключевым словом indexed позволяет искать по ним с помощью фильтров, но не только - они начинают стоит меньше памяти. Секрет кроется в том, что indexed аргументы кладутся на стек, а обычные - в память. Стоимость новой памяти растет квадратично, и использование indexed параметров почти всегда сохранит газ.
#gas #optimization #hint
Ключевое слово event позволяет объявить события которая потом можно пробрасывать во время выполнения контракта, и эти события будут доступны извне. Помечание аргументов ключевым словом indexed позволяет искать по ним с помощью фильтров, но не только - они начинают стоит меньше памяти. Секрет кроется в том, что indexed аргументы кладутся на стек, а обычные - в память. Стоимость новой памяти растет квадратично, и использование indexed параметров почти всегда сохранит газ.
#gas #optimization #hint
👍1
Оптимизация газа - 18
Ну, и в завершении, несколько дополнительных общих рекомендаций:
1. Используйте последние версии Solidity, так как они более безопасные и оптимизированные по газу;
2. Не используйте длинные string в revert(condition, string).
3. Лучше использовать кастомные Error, так как они дешевле по газу и стоимости деплоя. Более того, им можно дать подробное описание для других разработчиков в natspec;
Следите за циклами:
4) Чтобы не было dead функций:
if(x < 1) {
if(x > 2) {
return x;
}
}
5) Чтобы не было не нужны частей:
if(x > 1) {
if(x > 0) {
return x;
}
}
6) Чтобы не было не нужных циклов:
function constantOutcome() public pure returns(uint) {
uint num = 0;
for(uint i = 0; i < 100; i++) {
num += 1;
}
return num;
}
7) Чтобы не было overflow:
for (uint256 i = 0; i < length; ) {
// do something that doesn't change the value of i
unchecked {
i++;
}
}
Другие способы оптимизации газа ранее были на канале и будут еще. Следите за хештегами и присылайте свои заметки.
#gas #optimization #hint
Ну, и в завершении, несколько дополнительных общих рекомендаций:
1. Используйте последние версии Solidity, так как они более безопасные и оптимизированные по газу;
2. Не используйте длинные string в revert(condition, string).
3. Лучше использовать кастомные Error, так как они дешевле по газу и стоимости деплоя. Более того, им можно дать подробное описание для других разработчиков в natspec;
Следите за циклами:
4) Чтобы не было dead функций:
if(x < 1) {
if(x > 2) {
return x;
}
}
5) Чтобы не было не нужны частей:
if(x > 1) {
if(x > 0) {
return x;
}
}
6) Чтобы не было не нужных циклов:
function constantOutcome() public pure returns(uint) {
uint num = 0;
for(uint i = 0; i < 100; i++) {
num += 1;
}
return num;
}
7) Чтобы не было overflow:
for (uint256 i = 0; i < length; ) {
// do something that doesn't change the value of i
unchecked {
i++;
}
}
Другие способы оптимизации газа ранее были на канале и будут еще. Следите за хештегами и присылайте свои заметки.
#gas #optimization #hint
Гайд_по_проверке_контракта_и_его_кода.pdf
440.4 KB
Гайд по проверке контракта
За эту неделю я прочитал огромное количество информации по безопасности, включая все материалы из roadmap, который публиковал в начале этой недели.
Из всего я понял два основных момента:
1. Контракты строятся на максимальном zero-trust. Ни кому не доверять и проверять каждый шаг пользователя.
2. В контракте должны быть роли доступа. Самый простой пример это owner и другие пользователи. Если функцию, которую должен вызывать только owner, не защитить, то злоумышленник найдет способы, как пройти проверки и вызвать ее.
Из всех материалов в roadmap, меня привлек этот прекрасный гайд для проверки своего смарт контракта. Если пройтись по всем вопросам внимательно, то безопасность вашего контракта возрастет в несколько раз.
Предлагаю его перевод для всех.
#гайд #проверка
За эту неделю я прочитал огромное количество информации по безопасности, включая все материалы из roadmap, который публиковал в начале этой недели.
Из всего я понял два основных момента:
1. Контракты строятся на максимальном zero-trust. Ни кому не доверять и проверять каждый шаг пользователя.
2. В контракте должны быть роли доступа. Самый простой пример это owner и другие пользователи. Если функцию, которую должен вызывать только owner, не защитить, то злоумышленник найдет способы, как пройти проверки и вызвать ее.
Из всех материалов в roadmap, меня привлек этот прекрасный гайд для проверки своего смарт контракта. Если пройтись по всем вопросам внимательно, то безопасность вашего контракта возрастет в несколько раз.
Предлагаю его перевод для всех.
#гайд #проверка
👍4🔥1
Новая неделя - старые задачи
Привет всем!
На этой недели мы продолжим разбирать задачи с ethernaut и скорее всего начнем разбор нескольких с damn vulnerable defi.
Я еще не настолько хорош в этом, поэтому на каждую задачу трачу около 2-3 часов, включая изучение кода, поиск уязвимости, поиск решения, описание решения и пост. И к 3 задаче голова уже отказывается воспринимать новую информацию.
Тем не менее, в сети я нашел несколько интересных статей, которые также постараюсь переводить в течение недели.
Еще хочу узнать, пользуетесь ли вы плагинами в своем редакторе кода? Нужно ли составить краткую подборку по существующим решениям?
Всем приятной недели и легкого обучения!
Привет всем!
На этой недели мы продолжим разбирать задачи с ethernaut и скорее всего начнем разбор нескольких с damn vulnerable defi.
Я еще не настолько хорош в этом, поэтому на каждую задачу трачу около 2-3 часов, включая изучение кода, поиск уязвимости, поиск решения, описание решения и пост. И к 3 задаче голова уже отказывается воспринимать новую информацию.
Тем не менее, в сети я нашел несколько интересных статей, которые также постараюсь переводить в течение недели.
Еще хочу узнать, пользуетесь ли вы плагинами в своем редакторе кода? Нужно ли составить краткую подборку по существующим решениям?
Всем приятной недели и легкого обучения!
👍1
Ethernaut. Задача 19. Alien Codex
В этой задаче нам нужно завладеть правами owner.
Ссылка на задачу
В чем суть?
Задача не актуальная для версий языка выше 0.6, потому что метод .length стал read-only.
В самом контракте AlienCodex нет никаких намеков на переменную owner, но если мы развернем контракт, то увидим, что она наследуется из другого контракта Ownable.
По сути, это еще одна задача на работу с памятью контракта и overflow.
Если разобрать контракты внимательнее, то заметим, что переменные owner и contract помещаются в 32 байта, а значит лежат в нулевом слоте памяти. Массив codex лежит уже во втором слоте.
Как намек на дальнейшие действия, нам дана функция retract(), которая уменьшает длину массива. Хотя казалось бы, зачем его уменьшать, когда и так в нем ничего нет.
Однако, выполнив данную функцию, мы вызовем переполнение и получим доступ ко всей памяти контракта. Мы же помним, что максимальная память контракта равна (2 ** 256) - 1?
Затем нам останется всего лишь перевести новый адрес владельца в bytes32, так как revise() принимает этот аргумент, и вызвать поочередно функции AlienCodex контракта (пройти модификатор, вызвать переполнение, записать owner):
contract AlienHack {
AlienCodex alien;
constructor (address _alien) public{
alien = AlienCodex(_alien);
}
function exploit () external {
uint index = ((2 ** 256) - 1) - uint(keccak256(abi.encode(1))) + 1;
bytes32 myAddress = bytes32(uint256(uint160(tx.origin)));
alien.make_contact();
alien.retract();
alien.revise(index, myAddress);
}
}
В index мы просто таким образом вычисляем нулевой слот, где и лежит переменная owner.
#ethernaut
В этой задаче нам нужно завладеть правами owner.
Ссылка на задачу
В чем суть?
Задача не актуальная для версий языка выше 0.6, потому что метод .length стал read-only.
В самом контракте AlienCodex нет никаких намеков на переменную owner, но если мы развернем контракт, то увидим, что она наследуется из другого контракта Ownable.
По сути, это еще одна задача на работу с памятью контракта и overflow.
Если разобрать контракты внимательнее, то заметим, что переменные owner и contract помещаются в 32 байта, а значит лежат в нулевом слоте памяти. Массив codex лежит уже во втором слоте.
Как намек на дальнейшие действия, нам дана функция retract(), которая уменьшает длину массива. Хотя казалось бы, зачем его уменьшать, когда и так в нем ничего нет.
Однако, выполнив данную функцию, мы вызовем переполнение и получим доступ ко всей памяти контракта. Мы же помним, что максимальная память контракта равна (2 ** 256) - 1?
Затем нам останется всего лишь перевести новый адрес владельца в bytes32, так как revise() принимает этот аргумент, и вызвать поочередно функции AlienCodex контракта (пройти модификатор, вызвать переполнение, записать owner):
contract AlienHack {
AlienCodex alien;
constructor (address _alien) public{
alien = AlienCodex(_alien);
}
function exploit () external {
uint index = ((2 ** 256) - 1) - uint(keccak256(abi.encode(1))) + 1;
bytes32 myAddress = bytes32(uint256(uint160(tx.origin)));
alien.make_contact();
alien.retract();
alien.revise(index, myAddress);
}
}
В index мы просто таким образом вычисляем нулевой слот, где и лежит переменная owner.
#ethernaut