Необычное использование прокси
В еще одном конкурсном аудите обнаружил для себя необычный подход к использованию прокси контрактов, которое немного перевернуло мое представление об этом паттерне.
Смотрите, в обычной реализации мы делаем как? Мы пишем прокси контракт, а затем добавляем к нему контракт Логики. Все данные хранятся в прокси, а Логику мы можем менять хоть каждый день.
В проекте Sparkn по-другому.
P.S. Репо, возможно, будет открыто только на время конкурса.
Здесь три контракта: proxy, distributor и proxyFactory.
Proxy - самая простая реализация прокси паттерна с одной лишь fallback функцией.
Distributor - контракт Логики для прокси с разными функциями, основная из которых это перечисление средств пользователям.
ProxyFactory - основной контракт для взаимодействия.
Смотрите, что получается. Контракты proxyFactory и distributor постоянные. Т.е. distributor хоть и является контрактом Логики для прокси, но в данном случае он не обновляемый, так как функционал для этого не заложен.
В proxyFactor есть функция для деплоя прокси контракта и определения его адреса наперед, основываясь на определенных параметрах, в том числе на адресе пользователя.
Итак, суть в том, что пользователь может заранее вычислить свой адрес прокси и отправить туда некоторую сумму для распределения пользователям. Затем он вызывает функцию деплоя proxy, сразу после чего идет туда вызов и с помощью delegatecall вызывается функция из distributor, которая делает рассылку активов для установленных пользователей.
Другими словами, каждый взятый пользователь может сделать деплой прокси, на котором будут лежать токены, и уже с него через фиксированный контракт distributor сделать рассылку.
Два контракта постоянных, и неограниченное количество индивидуальных контрактов для пользователей.
На мой взгляд сделано очень круто!
#proxy
В еще одном конкурсном аудите обнаружил для себя необычный подход к использованию прокси контрактов, которое немного перевернуло мое представление об этом паттерне.
Смотрите, в обычной реализации мы делаем как? Мы пишем прокси контракт, а затем добавляем к нему контракт Логики. Все данные хранятся в прокси, а Логику мы можем менять хоть каждый день.
В проекте Sparkn по-другому.
P.S. Репо, возможно, будет открыто только на время конкурса.
Здесь три контракта: proxy, distributor и proxyFactory.
Proxy - самая простая реализация прокси паттерна с одной лишь fallback функцией.
Distributor - контракт Логики для прокси с разными функциями, основная из которых это перечисление средств пользователям.
ProxyFactory - основной контракт для взаимодействия.
Смотрите, что получается. Контракты proxyFactory и distributor постоянные. Т.е. distributor хоть и является контрактом Логики для прокси, но в данном случае он не обновляемый, так как функционал для этого не заложен.
В proxyFactor есть функция для деплоя прокси контракта и определения его адреса наперед, основываясь на определенных параметрах, в том числе на адресе пользователя.
Итак, суть в том, что пользователь может заранее вычислить свой адрес прокси и отправить туда некоторую сумму для распределения пользователям. Затем он вызывает функцию деплоя proxy, сразу после чего идет туда вызов и с помощью delegatecall вызывается функция из distributor, которая делает рассылку активов для установленных пользователей.
Другими словами, каждый взятый пользователь может сделать деплой прокси, на котором будут лежать токены, и уже с него через фиксированный контракт distributor сделать рассылку.
Два контракта постоянных, и неограниченное количество индивидуальных контрактов для пользователей.
На мой взгляд сделано очень круто!
#proxy
👍5
Еще один пример работы с Error
В протоколе Dopex встретил хороший пример работы с ошибками (require / error) в контракте.
Была создана специальная функция для валидации входящих условий:
function _validate(bool _clause, uint256 _errorCode) internal pure {
if (!_clause) revert RdpxV2CoreError(_errorCode);
}
которая проверяет условие в родительской функции, и если не true, то порождает Error с нужным кодом. Сам Error выглядит максимально просто:
error RdpxV2CoreError(uint256);
ну, и в коде, в комментариях, есть список ошибок по номеру, например:
// ERROR CODES
// E1: "Insufficient bond amount",
// E2: "Bond has expired",
// E3: "Invalid parameters"
Как это работает? Например, у нас есть функция, которая должна принимать адрес в качестве аргумента:
function getAddress(address addr) external {...}
и нам нужно проверить, чтобы этот адрес не был нулевым, поэтому мы передаем условие в функцию _validate():
function getAddress(address addr) external {
_valudate(addr != address(0), 4);
}
Идет проверка, если адрес окажется нулевым, по код выдаст ошибку под номером 4, из чего мы узнаем о проваленной проверке.
Вот такое простое решение.
#error
В протоколе Dopex встретил хороший пример работы с ошибками (require / error) в контракте.
Была создана специальная функция для валидации входящих условий:
function _validate(bool _clause, uint256 _errorCode) internal pure {
if (!_clause) revert RdpxV2CoreError(_errorCode);
}
которая проверяет условие в родительской функции, и если не true, то порождает Error с нужным кодом. Сам Error выглядит максимально просто:
error RdpxV2CoreError(uint256);
ну, и в коде, в комментариях, есть список ошибок по номеру, например:
// ERROR CODES
// E1: "Insufficient bond amount",
// E2: "Bond has expired",
// E3: "Invalid parameters"
Как это работает? Например, у нас есть функция, которая должна принимать адрес в качестве аргумента:
function getAddress(address addr) external {...}
и нам нужно проверить, чтобы этот адрес не был нулевым, поэтому мы передаем условие в функцию _validate():
function getAddress(address addr) external {
_valudate(addr != address(0), 4);
}
Идет проверка, если адрес окажется нулевым, по код выдаст ошибку под номером 4, из чего мы узнаем о проваленной проверке.
Вот такое простое решение.
#error
👍5
Снимоку.PNG
2.6 KB
1000+ участников!
С таким активным летом на события в жизни, совсем пропустил, что канал перевалил за 1000 участников! Потихоньку мы идем к цели и становимся самым большим каналом в русскоязычном сегменте, который посвящен Solidity и аудиту!
800+ информационных постов, 350+ ссылок на различные ресурсы, 2 активных обучающих модуля и 3 в плане, а также куча разных подборок - все это и делает наше сообщество одним из самых крутых!
С нового учебного года мы продолжим наш путь в тонкости языка и проблем безопасности современных смарт контрактов, будем говорить и про различные тулзы, которые стали появляться чуть ли не каждую неделю, и про опкоды и yul, и, возможно, попробуем залезть в ноду!
Ожидается много интересного!
А пока, гуляем последние дни лета, готовимся к новому сезону и копим силы!
Поздравляю всех с 1К!
С таким активным летом на события в жизни, совсем пропустил, что канал перевалил за 1000 участников! Потихоньку мы идем к цели и становимся самым большим каналом в русскоязычном сегменте, который посвящен Solidity и аудиту!
800+ информационных постов, 350+ ссылок на различные ресурсы, 2 активных обучающих модуля и 3 в плане, а также куча разных подборок - все это и делает наше сообщество одним из самых крутых!
С нового учебного года мы продолжим наш путь в тонкости языка и проблем безопасности современных смарт контрактов, будем говорить и про различные тулзы, которые стали появляться чуть ли не каждую неделю, и про опкоды и yul, и, возможно, попробуем залезть в ноду!
Ожидается много интересного!
А пока, гуляем последние дни лета, готовимся к новому сезону и копим силы!
Поздравляю всех с 1К!
🎉36🔥3
Есть ли авторы?
Слушайте, сейчас у меня пошел какой-то нереальный загруз в работе, что я не успеваю подготавливать хороший материал по темам языка и аудита для канала.
Я подумал, может кто-то из вас хотел бы попробовать написать несколько постов для канала? Может у вас есть какие-то любимые темы, которыми вы хотели бы поделиться, и которых еще не было на канале?
Также хотелось бы видеть авторские посты, а не копипаста с других каналов или статей. Посты с пруфами, ссылками и репо.
Есть желающие?
Слушайте, сейчас у меня пошел какой-то нереальный загруз в работе, что я не успеваю подготавливать хороший материал по темам языка и аудита для канала.
Я подумал, может кто-то из вас хотел бы попробовать написать несколько постов для канала? Может у вас есть какие-то любимые темы, которыми вы хотели бы поделиться, и которых еще не было на канале?
Также хотелось бы видеть авторские посты, а не копипаста с других каналов или статей. Посты с пруфами, ссылками и репо.
Есть желающие?
Пишешь тесты на Foundry?
Еще один вопрос на сегодня: есть ли среди участников канала те, кто хорошо умеет работать с Foundry и писать тесты?
Важно умение писать тесты для токенов, NFT, Vaults, связки с uniswap и другими defi, варианты с флешзаймами и т.д. Т.е. прям хорошо владеющие этим навыком!
Хочу попробовать один проект запустить осенью или зимой на зарубежный рынок.
Ниже будет опрос, кликните, кто на скиле)
Еще один вопрос на сегодня: есть ли среди участников канала те, кто хорошо умеет работать с Foundry и писать тесты?
Важно умение писать тесты для токенов, NFT, Vaults, связки с uniswap и другими defi, варианты с флешзаймами и т.д. Т.е. прям хорошо владеющие этим навыком!
Хочу попробовать один проект запустить осенью или зимой на зарубежный рынок.
Ниже будет опрос, кликните, кто на скиле)
👍6
Пишешь тесты на Foundry
Anonymous Poll
14%
Да, уже писал тесты и хорошо в них разбираюсь
19%
Да, на среднем уровне, нужно подучить
43%
Нет, но хочу научиться
24%
Нет, больше по hardhat
Пишешь тесты на Foundry 2
Пока выдалась свободная минута, хочу продолжить мысль про последние посты.
Из опроса видно, что 15 участников чата уверены в своих силах для написания тестов на Foundry. Честно говоря, я думал, что будет куда меньше!
Зачем я проводил опрос?
При общении с зарубежными разработчиками, я понял, что есть некоторый интерес к коммерческому написанию тестов для смарт контрактов, без разработки самих протоколов. Другими словами, вам дают контракт и вы пишите тесты для них.
И я планирую вывести эту услугу осенью-зимой, вкупе с парой других сопутствующих.
Нужны 3-4 человека, которым интересная эта тема и которые будут готовы к такой работе.
Если хотите поучаствовать, то напишите мне в личку @zaevlad. Желательно приложить ссылку на свой репо, где уже есть написанные тесты.
На данный момент точно по срокам не скажу, разбираю свои текущие задачи. Сейчас просто собираю желающих и проверяю интерес.
Всем спасибо за пройденный опрос!
Для тех, кто не знает Foundry, могу найти хорошего преподавателя, который даст уроки. Но это уже в конце года, сейчас еще идет текущий курс.
#foundry
Пока выдалась свободная минута, хочу продолжить мысль про последние посты.
Из опроса видно, что 15 участников чата уверены в своих силах для написания тестов на Foundry. Честно говоря, я думал, что будет куда меньше!
Зачем я проводил опрос?
При общении с зарубежными разработчиками, я понял, что есть некоторый интерес к коммерческому написанию тестов для смарт контрактов, без разработки самих протоколов. Другими словами, вам дают контракт и вы пишите тесты для них.
И я планирую вывести эту услугу осенью-зимой, вкупе с парой других сопутствующих.
Нужны 3-4 человека, которым интересная эта тема и которые будут готовы к такой работе.
Если хотите поучаствовать, то напишите мне в личку @zaevlad. Желательно приложить ссылку на свой репо, где уже есть написанные тесты.
На данный момент точно по срокам не скажу, разбираю свои текущие задачи. Сейчас просто собираю желающих и проверяю интерес.
Всем спасибо за пройденный опрос!
Для тех, кто не знает Foundry, могу найти хорошего преподавателя, который даст уроки. Но это уже в конце года, сейчас еще идет текущий курс.
#foundry
🔥7
Активное выдалось лето
Я тут вчера рефлексировал по прошедшему лету и удивлялся, что оно так быстро прошло. В последние дни все чаще появлялось чувство усталости (может и из-за сезонной аллергии) и ощущение, что я немного торможу свое собственное обучение.
Потом я решил посмотреть, а чем же я занимался все лето, и вот, что получилось:
1. За три месяца я поучаствовал в 7 конкурсных аудитах на Code4rena;
2. Заходил на 3 конкурса на новой платформе CodeHawks;
3. Запустил и написал материалы для 2 модулей своего курса для начинающих разработчиков;
4. Пошел отбор и стажировался в крутой зарубежной компании, занимающейся безопасностью и аудитом;
5. На канале стало 1000+ участников!
Начинается новый сезон, и я думаю, взять небольшой отпуск от конкурсных аудитов. Они занимаю очень много времени.
Хочу пересмотреть и изучить материалы, которые все это время сохранял к себе в закладки, провести 3 модуль курса и подготовить почву для дальнейших проектов.
Это так, просто делюсь с вами текущими событиями.
P.S. Все также еще актуален отклик на тестировщика Foundry!
Я тут вчера рефлексировал по прошедшему лету и удивлялся, что оно так быстро прошло. В последние дни все чаще появлялось чувство усталости (может и из-за сезонной аллергии) и ощущение, что я немного торможу свое собственное обучение.
Потом я решил посмотреть, а чем же я занимался все лето, и вот, что получилось:
1. За три месяца я поучаствовал в 7 конкурсных аудитах на Code4rena;
2. Заходил на 3 конкурса на новой платформе CodeHawks;
3. Запустил и написал материалы для 2 модулей своего курса для начинающих разработчиков;
4. Пошел отбор и стажировался в крутой зарубежной компании, занимающейся безопасностью и аудитом;
5. На канале стало 1000+ участников!
Начинается новый сезон, и я думаю, взять небольшой отпуск от конкурсных аудитов. Они занимаю очень много времени.
Хочу пересмотреть и изучить материалы, которые все это время сохранял к себе в закладки, провести 3 модуль курса и подготовить почву для дальнейших проектов.
Это так, просто делюсь с вами текущими событиями.
P.S. Все также еще актуален отклик на тестировщика Foundry!
🔥24👍5
Реентранси, рекурсия и ресмысление...
Каждый, кто начинает свой путь в аудите и безопасности смарт контрактов, одной из первых атак изучает Реентранси, грубо говоря, повторное вхождение в одну функцию несколько раз.
Я уже не раз писал на канале о этом, находил сам в задачах и аудитах, но что-то никогда не задумывался, как она работает "под капотом".
И вот вчера, меня немного поставили в ступор простым кодом:
Вопрос был простой: "Почему не работает реентранси?".
И код написан правильно. Но вот, почему не проходит...
Я пошел сравнивать этот код с кодом примеров данной атаки, которые были в разных статьях в поиске гугл. Достаточно большое количество ресурсов, в том числе solidity-by-example, показывали пример атаки, где в итоге баланс пользователя обнуляется, а не минусуется, как в данном примере. Другими словами, вместо:
balances[msg.sender] -= _amount;
было
balances[msg.sender] = 0;
И вот если в данном контракте также обнулять контракт, то атака работает!
Мне потребовалось некоторое время и помощь коллег, чтобы разобраться с этим.
Скажу сразу, я не правильно понимал ход выполнения атаки реентранси. До сего дня я полагал, что функция будет заходить в withdraw(), доходить до call вызова, перебрасываться в контракт хакера в receive(), а оттуда снова в withdraw() и так по кругу, пока не будет выполнено условие if(address(walletRe).balance >= SUM) {}, после чего call наконец пройдет, и исполнение функции дойдет до balances[msg.sender] -= _amount.
Короче, это не так.
На самом деле функция заходит в некую рекурсию, т.е. выполняется withdraw() полностью, но в своем программном порядке.
Лучше всего о рекурсии можно узнать из этого видео, спасибо @elawbek за наводку!
Смотрите, что получается. Допустим у нас есть 10 Эфиров на контракте Wallet, которые мы хотим увести. На контракте хакера 1 Эфир для атаки.
Сначала мы делаем депозит и переводим 1 Эфир с контракта хакера, на контракт Wallet, и так теперь 11 Эфира. После чего мы запускаем функцию attack().
Мы попадаем в withdraw(), происходит рекурсия, которая под капотом исполняет данную функцию 11 раз, так как всего там 11 Эфира лежит. И когда рекурсия начинает "сворачиваться", то оказывается, что успешная пересылка Эфира прошла всего один раз, и тогда уже сработало balances[msg.sender] -= _amount. В каждый последующий раз мы вычитали 1 Эфир из balances[msg.sender], на котором уже был ноль. Происходило переполнение и call вызов возвращал "External call returned false".
Именно поэтому атака реентранси тут и стопорилась!
Но почему в некоторых статьях также можно встретить описание атаки, где присутствует balances[msg.sender] -= _amount? Как, например, тут.
Объяснение просто: все дело в pragma и версии Solidity. До 0.8 версии в математических расчетах не было проверки на overflow. Система не выдавала ошибку, когда программа пыталась вычесть какое-либо число из 0.
Кстати, задача Ethernaut именно на этом и построена!
Каждый, кто начинает свой путь в аудите и безопасности смарт контрактов, одной из первых атак изучает Реентранси, грубо говоря, повторное вхождение в одну функцию несколько раз.
Я уже не раз писал на канале о этом, находил сам в задачах и аудитах, но что-то никогда не задумывался, как она работает "под капотом".
И вот вчера, меня немного поставили в ступор простым кодом:
contract Wallet {
mapping(address => uint) public balances;
function deposit(address _to) public payable {
balances[_to] = balances[_to] + msg.value;
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value:_amount}("");
require(result, "External call returned false");
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
contract AttacWallet {
uint constant SUM = 1 ether;
Wallet walletRe;
constructor(address payable _wallet) {
walletRe = Wallet(_wallet);
}
function depositAttack() public payable {
walletRe.deposit{value: SUM}(address(this));
}
function attack() external payable {
walletRe.withdraw(SUM);
}
receive() external payable {
if(address(walletRe).balance >= SUM) {
walletRe.withdraw(SUM);
}
}
}
Есть два контракта: Wallet и AttacWalet, второй предназначен для взлома первого.Вопрос был простой: "Почему не работает реентранси?".
И код написан правильно. Но вот, почему не проходит...
Я пошел сравнивать этот код с кодом примеров данной атаки, которые были в разных статьях в поиске гугл. Достаточно большое количество ресурсов, в том числе solidity-by-example, показывали пример атаки, где в итоге баланс пользователя обнуляется, а не минусуется, как в данном примере. Другими словами, вместо:
balances[msg.sender] -= _amount;
было
balances[msg.sender] = 0;
И вот если в данном контракте также обнулять контракт, то атака работает!
Мне потребовалось некоторое время и помощь коллег, чтобы разобраться с этим.
Скажу сразу, я не правильно понимал ход выполнения атаки реентранси. До сего дня я полагал, что функция будет заходить в withdraw(), доходить до call вызова, перебрасываться в контракт хакера в receive(), а оттуда снова в withdraw() и так по кругу, пока не будет выполнено условие if(address(walletRe).balance >= SUM) {}, после чего call наконец пройдет, и исполнение функции дойдет до balances[msg.sender] -= _amount.
Короче, это не так.
На самом деле функция заходит в некую рекурсию, т.е. выполняется withdraw() полностью, но в своем программном порядке.
Лучше всего о рекурсии можно узнать из этого видео, спасибо @elawbek за наводку!
Смотрите, что получается. Допустим у нас есть 10 Эфиров на контракте Wallet, которые мы хотим увести. На контракте хакера 1 Эфир для атаки.
Сначала мы делаем депозит и переводим 1 Эфир с контракта хакера, на контракт Wallet, и так теперь 11 Эфира. После чего мы запускаем функцию attack().
Мы попадаем в withdraw(), происходит рекурсия, которая под капотом исполняет данную функцию 11 раз, так как всего там 11 Эфира лежит. И когда рекурсия начинает "сворачиваться", то оказывается, что успешная пересылка Эфира прошла всего один раз, и тогда уже сработало balances[msg.sender] -= _amount. В каждый последующий раз мы вычитали 1 Эфир из balances[msg.sender], на котором уже был ноль. Происходило переполнение и call вызов возвращал "External call returned false".
Именно поэтому атака реентранси тут и стопорилась!
Но почему в некоторых статьях также можно встретить описание атаки, где присутствует balances[msg.sender] -= _amount? Как, например, тут.
Объяснение просто: все дело в pragma и версии Solidity. До 0.8 версии в математических расчетах не было проверки на overflow. Система не выдавала ошибку, когда программа пыталась вычесть какое-либо число из 0.
Кстати, задача Ethernaut именно на этом и построена!
👍9❤2👏2😁1
Теперь же происходит переполнение и функция откатывается. Это можно легко проверить и в 0.8 версии поместив вычитание в unchecked:
В общем, такое получилось небольшое расследование. Если все так же не очень понятен смысл поста, то рекомендую сначала посмотреть видео про рекурсию, а потом еще раз почитать про реентранси.
P.S. Если захотите увидеть, как все происходит внутри, добавьте этот тест в Foundry:
forge test --match-contract TestReentrancy -vvvv
Надеюсь, некоторым, как и мне вчера, стало чуть более понятна эта атака.
#reentrancy
unchecked {
balances[msg.sender] -= _amount;
}
Реентранси снова пройдет! В общем, такое получилось небольшое расследование. Если все так же не очень понятен смысл поста, то рекомендую сначала посмотреть видео про рекурсию, а потом еще раз почитать про реентранси.
P.S. Если захотите увидеть, как все происходит внутри, добавьте этот тест в Foundry:
contract TestReentrancy is Test {
using stdStorage for StdStorage;
Wallet public wallet;
AttacWallet public attacWallet;
function setUp() public{
wallet = new Wallet();
attacWallet = new AttacWallet(payable(wallet));
vm.deal(address(wallet), 10 ether);
vm.deal(address(attacWallet), 2 ether);
}
function test_testSetup() public {
attacWallet.depositAttack();
attacWallet.attack();
console.log(address(attacWallet).balance);
}
}
И запустите команду в терминале:forge test --match-contract TestReentrancy -vvvv
Надеюсь, некоторым, как и мне вчера, стало чуть более понятна эта атака.
#reentrancy
👍13
Письменное задание в Spearbit
Для тех, кто не в курсе, Spearbit крутая зарубежная компания, которая занимается безопасностью смарт контрактов. Пару раз в год они набирают к себе аудиторов. Для этого требуется пройти тест из 4 вариантов ответа на время и после технического интервью еще выполнить письменное задание.
Предлагаю вашему вниманию одно из таких заданий от февраля 2022 года. Прекрасная практика для аудиторов и тех, кто любит решать задачи.
Есть два контракта: прокси и Логики. Деплой логики делается только однажды для всех пользователей. Прокси - для каждого свой.
Пользователи держат все свои активы на прокси контракте и, в случае необходимости, посылают вызовы на контракт Логики.
Тут есть критическая уязвимость. Задание: найти ее и дать свои рекомендации по ее устранению.
Вы получите дополнительный бонус, если в рекомендациях расскажите о нишевом решении для предотвращения уязвимости. Еще один бонус получите, если в рекомендациях расскажите, как можно убрать уязвимость, убрав два слова в контракте, и изменив всего одно.
Вот ссылка на репо задания: https://github.com/spearbit-audits/writing-exercise
Удачи в поисках!
#proxy #bug #spearbit
Для тех, кто не в курсе, Spearbit крутая зарубежная компания, которая занимается безопасностью смарт контрактов. Пару раз в год они набирают к себе аудиторов. Для этого требуется пройти тест из 4 вариантов ответа на время и после технического интервью еще выполнить письменное задание.
Предлагаю вашему вниманию одно из таких заданий от февраля 2022 года. Прекрасная практика для аудиторов и тех, кто любит решать задачи.
Есть два контракта: прокси и Логики. Деплой логики делается только однажды для всех пользователей. Прокси - для каждого свой.
Пользователи держат все свои активы на прокси контракте и, в случае необходимости, посылают вызовы на контракт Логики.
Тут есть критическая уязвимость. Задание: найти ее и дать свои рекомендации по ее устранению.
Вы получите дополнительный бонус, если в рекомендациях расскажите о нишевом решении для предотвращения уязвимости. Еще один бонус получите, если в рекомендациях расскажите, как можно убрать уязвимость, убрав два слова в контракте, и изменив всего одно.
Вот ссылка на репо задания: https://github.com/spearbit-audits/writing-exercise
Удачи в поисках!
#proxy #bug #spearbit
👍11❤1🔥1
Что за адрес 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE?
Это специальный адрес, который работает как идентификатор для Эфира, нативной валюты. Подобно адресам токенов ERC20, данный адрес используется для представления Эфира в контрактах, как если он был токеном.
Его можно посмотреть на Etherscan тут.
#ether
Это специальный адрес, который работает как идентификатор для Эфира, нативной валюты. Подобно адресам токенов ERC20, данный адрес используется для представления Эфира в контрактах, как если он был токеном.
Его можно посмотреть на Etherscan тут.
#ether
👍7
Побитовые в Chainlink
На этой неделе заканчивается конкурсный аудит в Chainlink. И в одном из контрактов были использованы побитовые операции, которые я и хочу сегодня разобрать с вами.
Вообще, побитовые операции отдельная моя боль. Я пока что еще не научился понимать их на интуитивном уровне, и каждый код с ними занимает некоторое время, чтобы полноценно понимать его работу.
Контракты конкурсного аудита можно посмотреть тут, а тут ссылка на контракт и функцию из примера.
Итак, посмотрим на функцию с побитовой операцией:
(uint224(uint112(latestPrincipal)) << 112) - сначала значение latestPrincipal уменьшается до uint112, а затем увеличивается до uint224, для того чтобы вместить значение для операции сдвига влево.
uint224(uint112(latestStakedAtTime)) - берем значение latestStakedAtTime и также приводим его к uint112 в начале, и к uint224 позже.
Побитовая операция OR (вот эта палочка - "|" между значениями) служит для объединения ранее сдвинутого latestPrincipal со latestStakedAtTime.
В результате получается, что latestPrincipal занимает верхние 112 бит, а latestStakedAtTime - нижние 112 бит.
Для себя и тех, кто забыл, напомню, как работает побитовое OR (ИЛИ).
Допустим у нас есть два значения:
Это делается для того, чтобы работа со схожими по смыслу значениями была более эффективной и занимала меньше места в памяти контракта.
Прекрасное компактное решение от команды Chainlink!
#bit #or #shift
На этой неделе заканчивается конкурсный аудит в Chainlink. И в одном из контрактов были использованы побитовые операции, которые я и хочу сегодня разобрать с вами.
Вообще, побитовые операции отдельная моя боль. Я пока что еще не научился понимать их на интуитивном уровне, и каждый код с ними занимает некоторое время, чтобы полноценно понимать его работу.
Контракты конкурсного аудита можно посмотреть тут, а тут ссылка на контракт и функцию из примера.
Итак, посмотрим на функцию с побитовой операцией:
function _updateStakerHistory(s_checkpointId - это просто уникальный идентифкатор для ведения учета, который увеличивается на +1 при каждом вызове функции.
Staker storage staker,
uint256 latestPrincipal,
uint256 latestStakedAtTime
) internal {
staker.history.push(
s_checkpointId++,
(uint224(uint112(latestPrincipal)) << 112) | uint224(uint112(latestStakedAtTime))
);
}
(uint224(uint112(latestPrincipal)) << 112) - сначала значение latestPrincipal уменьшается до uint112, а затем увеличивается до uint224, для того чтобы вместить значение для операции сдвига влево.
uint224(uint112(latestStakedAtTime)) - берем значение latestStakedAtTime и также приводим его к uint112 в начале, и к uint224 позже.
Побитовая операция OR (вот эта палочка - "|" между значениями) служит для объединения ранее сдвинутого latestPrincipal со latestStakedAtTime.
В результате получается, что latestPrincipal занимает верхние 112 бит, а latestStakedAtTime - нижние 112 бит.
Для себя и тех, кто забыл, напомню, как работает побитовое OR (ИЛИ).
Допустим у нас есть два значения:
а 1011010101Если хотябы одно значение будет равно "1", то и результат будет равен "1". Отсюда получаем:
b 0111010111
с 1111010111Т.е. вы поняли теперь как работает функция в chainlink? Мы берем значение, обрезаем его до uint112 и тут же увеличивает до uint224, освобождая место при помощи сдвига влево (<<) для другого значения, которое мы и записываем на освободившееся пространство.
Это делается для того, чтобы работа со схожими по смыслу значениями была более эффективной и занимала меньше места в памяти контракта.
Прекрасное компактное решение от команды Chainlink!
#bit #or #shift
👍4🔥3
Плагин для VSCode
Некоторое время назад подбирал для себя простое решения для того, чтобы отмечать файлы и контракты, аудит которых уже провел.
Многие из плагинов были слегка накрученные и с множеством дополнительных функций, что порой, скорее, мешало работе, чем помогало ей.
Вот нашел для себя простой плагин Mark files:
После установки, кликаете правой кнопкой мыши на древе файлов и в появившемся меню выбираете "Mark \ unmark selected file". Радом с файлом или папкой появится маленький значок.
В общем, крайне простой инструмент.
#plaggin
Некоторое время назад подбирал для себя простое решения для того, чтобы отмечать файлы и контракты, аудит которых уже провел.
Многие из плагинов были слегка накрученные и с множеством дополнительных функций, что порой, скорее, мешало работе, чем помогало ей.
Вот нашел для себя простой плагин Mark files:
После установки, кликаете правой кнопкой мыши на древе файлов и в появившемся меню выбираете "Mark \ unmark selected file". Радом с файлом или папкой появится маленький значок.
В общем, крайне простой инструмент.
#plaggin
👍7
Return в Solidity и return в assembly
Знали ли вы, что return в assembly ведет себя по-другому, чем return в solidity?
В assembly return фактически является опкодом, который прекращает выполнение контекста и возвращает срез (часть информации) памяти.
Например, в функции:
В solidity "return <value>" как бы говорит компилятору, что функция завершила свое выполнение и <value> должно быть возвращено для следующего контекста.
Для external функций это, по сути, означает вызов Return, а для internal - типа "просто возвращайся".
Return в solidity служит как полезная абстракция и позволяет нашим функциям прекращаться раньше, порой избегая другую логику исполнения, как например тут:
Пост переведен из данной ветки Твиттера от philogy.
Фух, я еще постигаю assembly и мне крайне интересно, как работает вся эта штуковина изнутри.
#return #assembly
Знали ли вы, что return в assembly ведет себя по-другому, чем return в solidity?
В assembly return фактически является опкодом, который прекращает выполнение контекста и возвращает срез (часть информации) памяти.
Например, в функции:
function someLogic() external returns(bool success) {
assembly {
return(0x00, 0x20)
}
_someMoreLogic();
}
действие никогда не дойдет до _someMoreLogic(), прекратившись на участке assembly.В solidity "return <value>" как бы говорит компилятору, что функция завершила свое выполнение и <value> должно быть возвращено для следующего контекста.
Для external функций это, по сути, означает вызов Return, а для internal - типа "просто возвращайся".
Return в solidity служит как полезная абстракция и позволяет нашим функциям прекращаться раньше, порой избегая другую логику исполнения, как например тут:
function someLogic() internal {
if (isOwner()) return;
uint fee = calculateFee();
_charheFee();
}
Если же мы хотим создать подобную логику с помощью assembly, нам потребуется использовать for циклы:function someLogic() internal {
assembly{
for {} 1 {} {
if eq(caller(), sload(owner, slot)) {
break
}
let fee := calcFee()
break
}
}
}
В этом случае for {} 1 {} {} выступает эквивалентом while(true), и исполнение может прекратиться либо после первого if, при вополнении условий, либо уже в конце функции.Пост переведен из данной ветки Твиттера от philogy.
Фух, я еще постигаю assembly и мне крайне интересно, как работает вся эта штуковина изнутри.
#return #assembly
❤5👍1
Сборник советов по оптимизации газа
RareSkills выпустил отличный сборник с рекомендациями и примерами по оптимизации газа. Да, большинство из них уже будут находиться сканирующими ботами, но все же важно понимать как это устроено.
Всего приведено около 70 примеров! В общем, интересное чтиво в свободное время.
Сборник от RareSkills
Будет также полезно тем, кто пишет своего бота и детекторы на скан контрактов!
#gas
RareSkills выпустил отличный сборник с рекомендациями и примерами по оптимизации газа. Да, большинство из них уже будут находиться сканирующими ботами, но все же важно понимать как это устроено.
Всего приведено около 70 примеров! В общем, интересное чтиво в свободное время.
Сборник от RareSkills
Будет также полезно тем, кто пишет своего бота и детекторы на скан контрактов!
#gas
🔥4👍1
Баг в протоколе Lybra
Интересная находка была в конкурсном аудите протокола Lybra, в наборе которого были прокси контракты.
При всей очевидности проблемы, найти ее смог только один аудитор. Почему очевидной? Просто потому, что этому обучают всех с момента знакомства с прокси контрактами.
Для протокола был создан отдельный контракт конфигуратор, который так и называется LybraConfigurator. По своей сути, это был Логический контракт для прокси контракта.
Проблема заключалась в том, что тут был конструктор, с помощью которого устанавливались переменные в память, что и было ошибкой.
Как мы знаем, конструктор исполняется только один раз, во время деплоя контракта, и позволяет сделать изначальную установку переменных в контракт. Но запись идет в память данного контракта, а не в память прокси, как это положено в данном случае. Поэтому на момент? когда были бы развернуты контракты в сети, то две переменные в прокси остались бы с нулевым значением.
Вот такой очевидный и не очень баг.
Более детально о нем можно прочитать тут, в отчете code4rena.
Будьте внимательны при работе с прокси!
#proxy #constructor
Интересная находка была в конкурсном аудите протокола Lybra, в наборе которого были прокси контракты.
При всей очевидности проблемы, найти ее смог только один аудитор. Почему очевидной? Просто потому, что этому обучают всех с момента знакомства с прокси контрактами.
Для протокола был создан отдельный контракт конфигуратор, который так и называется LybraConfigurator. По своей сути, это был Логический контракт для прокси контракта.
Проблема заключалась в том, что тут был конструктор, с помощью которого устанавливались переменные в память, что и было ошибкой.
Как мы знаем, конструктор исполняется только один раз, во время деплоя контракта, и позволяет сделать изначальную установку переменных в контракт. Но запись идет в память данного контракта, а не в память прокси, как это положено в данном случае. Поэтому на момент? когда были бы развернуты контракты в сети, то две переменные в прокси остались бы с нулевым значением.
Вот такой очевидный и не очень баг.
Более детально о нем можно прочитать тут, в отчете code4rena.
Будьте внимательны при работе с прокси!
#proxy #constructor
👍4
Checks-Effects-Interactions устарел?
Наткнулся на интересную статью, где автор рассуждает над паттерном CEI и о том, что сейчас самое время сделать его чуть более безопасным.
В статье рассказывается о новом подходе к написанию безопасных функций, который включает в себя проверку инвариантов и называется FREI-PI (Function Requirements-Effects-Interactions + Protocol Invariants).
На самом деле, некоторые аудиторы уже могли встречать его, просматривая контракты таких протоколов, как dYdX, Compound или Aave.
В общем, почитать подробнее можно тут.
На мой взгляд, это вполне может вскоре стать отдельным пунктом для аудиторского отчета.
#freypi #cie
Наткнулся на интересную статью, где автор рассуждает над паттерном CEI и о том, что сейчас самое время сделать его чуть более безопасным.
В статье рассказывается о новом подходе к написанию безопасных функций, который включает в себя проверку инвариантов и называется FREI-PI (Function Requirements-Effects-Interactions + Protocol Invariants).
На самом деле, некоторые аудиторы уже могли встречать его, просматривая контракты таких протоколов, как dYdX, Compound или Aave.
В общем, почитать подробнее можно тут.
На мой взгляд, это вполне может вскоре стать отдельным пунктом для аудиторского отчета.
#freypi #cie
👍6
Повышаем мастерство с Forge coverage
Я уже несколько раз писал на канале, что в скором времени компании будут нанимать отдельных разработчиков для написания тестов для своих протоколов. А знания и навыки работы с Hardhat или Foundry будут стоять в описаниях вакансий с Solidity, как это сейчас с JS/React.
Вот и в этой статье рассказывается, что покрытие тестами на 100% не всегда означает, что проверены все возможные функции и линии в контракте. И разработчик должен уметь работать с отчетами покрытия, также как он работает с самими написаниями тестов.
Например, вы знали, что с foundry coverage можно запустить дополнительную команду report debug, которая покажет, какие строки и функции вы забыли протестировать?
Или, что с помощью forge coverage --report lcov можно сформировать отчет в html разметке?
Или, что, используя плагин Coverage Gutters, можно визуально в контракте увидеть код без тестов?
В общем, профессиональная команда тестировщиков должна будет выдавать отчет с абсолютным покрытием тестом и отчетом для клиента, где код будет светиться "зеленым" и везде будет 100% в таблице.
В статье даже есть репо, с которым вы можете потренироваться!
Советую всем и каждому повысить свои навыки в работе с Foundry.
#foundry #coverage
Я уже несколько раз писал на канале, что в скором времени компании будут нанимать отдельных разработчиков для написания тестов для своих протоколов. А знания и навыки работы с Hardhat или Foundry будут стоять в описаниях вакансий с Solidity, как это сейчас с JS/React.
Вот и в этой статье рассказывается, что покрытие тестами на 100% не всегда означает, что проверены все возможные функции и линии в контракте. И разработчик должен уметь работать с отчетами покрытия, также как он работает с самими написаниями тестов.
Например, вы знали, что с foundry coverage можно запустить дополнительную команду report debug, которая покажет, какие строки и функции вы забыли протестировать?
Или, что с помощью forge coverage --report lcov можно сформировать отчет в html разметке?
Или, что, используя плагин Coverage Gutters, можно визуально в контракте увидеть код без тестов?
В общем, профессиональная команда тестировщиков должна будет выдавать отчет с абсолютным покрытием тестом и отчетом для клиента, где код будет светиться "зеленым" и везде будет 100% в таблице.
В статье даже есть репо, с которым вы можете потренироваться!
Советую всем и каждому повысить свои навыки в работе с Foundry.
#foundry #coverage
🔥6
Более дешевый способ проверки на нулевой адрес
Встретил в Твиттере код, который якобы призвал сделать более дешевой проверку на нулевой адрес. Забил его в Ремикс, чтобы самому узреть эти гигантскую разницу...
Вот сам код:
А как вы относитесь к такой переоптимизации? Нужно ли бороться за каждую единицу газа в контракте?
#gas
Встретил в Твиттере код, который якобы призвал сделать более дешевой проверку на нулевой адрес. Забил его в Ремикс, чтобы самому узреть эти гигантскую разницу...
Вот сам код:
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
contract AssemblyAddressZero {
error ZeroAddress();
function expensiveZero(address toCheck) public returns(bool success) {
if(toCheck == address(0)) revert ZeroAddress();
return true;
}
function cheapZero(address toCheck) public returns(bool success) {
assembly {
if iszero(toCheck) {
let ptr := mload(0x40)
mstore(ptr, 0xd92e233d00000000000000000000000000000000000000000000000000000000)
revert(ptr, 0x4)
}
}
return true;
}
}
Разница в одном-двух десятке единиц газа... Хммм... А как вы относитесь к такой переоптимизации? Нужно ли бороться за каждую единицу газа в контракте?
#gas
😁4
Курс по Defi на Youtube
Не помню наверняка, но, вроде, несколько видео с данного цикла я уже выкладывал на канале. А на днях я переоткрыл его для себя.
Спустя некоторое время, когда я стал разбираться в протоколах и тем, что вообще происходит в контрактах DeFi, лекции из курса стали звучать немного по другому.
Сам курс, в 4 частях, на английском языке. Если начнете смотреть хотя бы по одному видео каждый день, то вам хватит до нового года!
Course I: DeFi Infrastructure
Course II: DeFi Primitives
Course III: DeFi Deep Dive
Course IV: DeFi Risks and Opportunities
Приятного просмотра!
#defi
Не помню наверняка, но, вроде, несколько видео с данного цикла я уже выкладывал на канале. А на днях я переоткрыл его для себя.
Спустя некоторое время, когда я стал разбираться в протоколах и тем, что вообще происходит в контрактах DeFi, лекции из курса стали звучать немного по другому.
Сам курс, в 4 частях, на английском языке. Если начнете смотреть хотя бы по одному видео каждый день, то вам хватит до нового года!
Course I: DeFi Infrastructure
Course II: DeFi Primitives
Course III: DeFi Deep Dive
Course IV: DeFi Risks and Opportunities
Приятного просмотра!
#defi
🔥19❤2👍1