Всех с новой рабочей неделей и сегодня мы разберем довольно интересный пункт из репо, который звучит как:
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