Solidity. Смарт контракты и аудит
2.62K subscribers
246 photos
7 videos
18 files
550 links
Обучение Solidity. Уроки, аудит, разбор кода и популярных сервисов
Download Telegram
Функция "fallback()" вызывается в том случае, если относительно смарт-контракта бала вызвана транзакция с неизвеным именем функции.

Это нужно для того чтобы, например, в случае, если на смарт-контракт придет транзакция с вызовом функции, которой нет, она не откатится, а покажет ошибку, которая была описана в "fallback".

Проще говоря, если к нам придет транзакция, которая запросит функцию возврата денег, а она не прописана у нас, то в "fallback" можно отловить этот момент и показать ошибку.

#fallback #функции #function
🔥1
Особенности fallback функций

Если fallback() указан как payable, то он может принимать Ether.

Особенность отправляющих Ether функций transfer и send в том, что у них есть ограничение - инициированная ими транзакция не должна расходовать больше, чем 2300 gas. Поэтому, если внутри fallback реализована какая-то сложная логика (вызвать еще какие-то функции, записать storage и т.д.) (при поступлении Ether с помощью transfer или send), то это будет стоить больше, чем 2300 и транзакция откатится.

Максимум на что хватит газа в такой ситуации внутри fallback - это на emit event.

При этом, если эфир отправляется с помощью call вызовов, то есть возможность повышения лимита газа, и тогда в fallback функция не откатит транзакцию.

Будьте аккуратны с этим, и всегда дополняйте fallback другими функциями, которые могут принимать деньги, например receive.

#hint #fallback #receive
Немного о фантомных функциях

Уже несколько раз в различных статьях видел упоминания о фантомных функциях в контрактах. И вот еще одна прекрасная статья, рассказывающая о том, что в контрактах токенов могут быть подобные функции, которые могут привести к взлому.

Сама статья на английском языке, но ее суть в том, что если в контракте токена, как например в WETH, нет функции permit(), но есть fallback(), то вызывая из вашего контракта weth.premit() - транзакция не откатится с ошибкой.

Это может быть уязвимостью, как было в примере с AnySwap/MultiChain.

Подробнее читаем в статье.

#phantom #fallback #permit
3
Solidity hints. Часть 20

Всех с новой рабочей неделей и сегодня мы разберем довольно интересный пункт из репо, который звучит как:

The data returned from fallback function will not be ABI-encoded. Instead it will be returned without modifications (not even padding) WTF

что в переводе:

Данные, возвращаемые функцией fallback, не будут кодироваться ABI. Вместо этого они будут возвращены без изменений (даже без падинга) ОМБ

Думаю, для начала напомню, что такое паддинг.

Допустим мы создали байтовый массив с фиксированной длиной bytes32. Если мы запишем туда значение, которое будет меньше 32 байтов, то его конец будет заполнен нулями, например:

bytes32: 0x5b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000

Тут мы записали адрес 5b38da6a701c568545dcfcb03fcb875f56beddc4, и так как размерность была 32, то оставшееся место заняли нули. Это и называется паддингом.

Он разделяется на два вида:

Right-padded - к нему относят такие типы данных как, string, bytes and bytesN (N - указание на размерность).

Left-padded - intN / uintN, address and другие типы.

Другими словами, в одном случае нули будут добавляться в конец, в другом - в начало.

Теперь перейдем к самим fallback функциям.

Во многих источниках в сети, я встречал следующие описания:

1. Она являются неименованными функциями.
2. Она не может принимать аргументы.
3. Она не может ничего возвращать.
3. В смарт-контракте может быть только одна такая функция.
4. Обязательно должна быть external.
5. Для приема Эфира должна иметь модификатор payable.
6. Ограничено лимитом в 2300 газа, если ее вызывают другие функции.
7. Исполняется, когда в контракте не найдена вызываемая функция.

И если 5 пунктов верны для всех случаем с fallback, то 2 и 3 не совсем верны, так как есть следующая форма функции:

fallback (bytes calldata input) external [payable] returns (bytes memory output)

т.е. они и принимает, и возвращает некоторые данные. Об этой форме и говорится в разбираемом пункте. Эта фишка появилась в функции в версии 0.7.6

На словах объяснить будет немного сложно, поэтому будет проще посмотреть примеры возвращаемых значений в Ремиксе в контрактах:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

contract FallbackInputOutput {
address immutable target;

constructor(address _target) {
target = _target;
}

fallback(bytes calldata data) external payable returns (bytes memory) {
(bool ok, bytes memory res) = target.call{value: msg.value}(data);
//require(ok, "call failed");
return res;
//return abi.encode(res);
}
}

contract Counter {
uint256 public count;

function get() external view returns (uint256) {
return count;
}

}

contract TestFallbackInputOutput {
event Log(bytes res);

function test(address _fallback, bytes calldata data) external {
(bool ok, bytes memory res) = _fallback.call(data);
require(ok, "call failed");
emit Log(res);
}

function getTestData() external pure returns (bytes memory) {
return
(abi.encodeCall(Counter.get, ()));
}
}


Сначала разворачиваются контракты TestFallbackInputOutput и Counter, а затем FallbackInputOutput от адреса Counter.

После деплоя вызываете getTestData() для получения банных для отправки, а затем test() с адресом FallbackInputOutput.

В первый раз попробуйте с простым return, а после с return abi.encode(res), и в логах посмотрите, какие данные возвращаются.

В первом случае вернуться незашифрованные данные, о чем и говорится в пункте. Если хотите как-то контролировать их получение, то нужно дополнительно использовать abi.encode для этого.

#fallback #output
🤯1