Сигнатура функции - это, по факту, просто строка, которая содержит имя функции и её аргументы (без указания на место хранения). К примеру,
"test(uint256)".Но вот когда мы обращаемся к этой функции в транзакции, мы не можем просто передать её сигнатуру в виде такой строки. Она должна быть правильно закодирована:
abi.encodeWithSignature("test(uint256)", 42)В эту же функцию кодирования мы можем сразу добавить передаваемые аргументы (в данном случае, 42).
Но тогда возникает вопрос: а как именно кодируется сама сигнатура? Ответ следующий:
bytes4(keccak256(bytes("test(uint256)")))То есть мы берём сигнатуру, считаем её хэш и забираем первые 4 байта этого хэша. Получившийся результат называется *селектор* и это именно, что мы используем для указания на вызываемую функцию. К примеру, если в вашем контракте есть функция
test, принимающая uint256, то в ABI вы увидите что-то вроде:"test(uint256)": "29e99f07"
По правую сторону как раз находится селектор этой функции.
При вызове функции первые 4 байта из calldata и будут содержать селектор функции. Это можно проверить либо так:
function test(uint a) external view returns(bytes4) {
return bytes4(msg.data[0:4]);
}Либо так:
function test(uint a) external view returns(bytes4) {
bytes32 temp;
assembly {
temp := calldataload(0)
}
return bytes4(temp);
}В любом случае результатом и будет тот самый
0x29e99f07. После первых 4 байт будут идти уже сами аргументы, которые передаются в функцию. function test(uint a) external view returns(bytes4, bytes32) {
bytes32 temp;
bytes32 _arg1;
assembly {
temp := calldataload(0)
_arg1 := calldataload(4)
}
return (bytes4(temp), _arg1);
}При вызове функции
test с аргументом 1 вернётся0:
bytes4: 0x29e99f07
1:
bytes32: 0x0000000000000000000000000000000000000000000000000000000000000001
Эта единичка как раз и видна в bytes32.
Правда всё несколько сложнее, к примеру, с массивами. То есть если у нас есть функция
function test(uint[] memory a) и мы вызываем её с аргументом [1,2] то в msg.data будет нечто странное:0xca1606840000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002
Да, первые 4 байта - это селектор, но зачем там какие-то двойки?
Сделаем следующее:
function test(uint[] memory a) external view returns(bytes4, bytes32, bytes32, bytes32, bytes32, bytes memory) {
bytes32 temp;
bytes32 _startIn;
bytes32 _elCount;
bytes32 _firstEl;
bytes32 _secondEl;
assembly {
temp := calldataload(0)
_startIn := calldataload(4)
_elCount := calldataload(36)
_firstEl := calldataload(68)
_secondEl := calldataload(100)
}
return (bytes4(temp), _startIn, _elCount, _firstEl, _secondEl, msg.data);
}Итак, после первых 4 байт идут блоки по bytes32.
* startIn - через сколько байт от начала хранится значение нашего аргумента (массива). Тут вернётся значение 0x20 (нули в начале пропускаю), что равно 32. То есть через 32 байта от начала calldata (плюс ещё 4 байта селектора) начинается наш массив
* elCount - сколько элементов массиве. Вернётся значение 0x00000...02 то есть 2 элемента
* firstEl - первый элемент массива, то есть 0x00000...01
* secondEl - второй элемент, 0x00000...02