Необычная функция
Просто хочу поделиться необычной логикой работы функции, которую я вчера встретил в аудите. За все время обучения лично я столкнулся с этим впервые, что вызвало некоторые недоумения.
Я постараюсь разложить ее "по полочкам". Итак, вот сам контракт, если вдруг кому будет интересно.
И там есть такая функция (я ее обрезал, убрав не интересные части):
function stake (
AssetType _assetType,
uint256 _timelockId,
uint256,
uint256,
uint256
) external nonReentrant {
uint256 timelockOption = timelockOptions[_assetType][_timelockId];
function (uint256) _s1 = _stakeS1Citizen;
function (uint256) _s2 = _stakeS2Citizen;
function (uint256) _b = _stakeBytes;
function (uint256) _lp = _stakeLP;
function (uint256) _stake;
assembly {
switch _assetType
case 0 {
_stake := _s1
}
case 1 {
_stake := _s2
}
case 2 {
_stake := _b
}
case 3 {
_stake := _lp
}
default {}
}
_stake(timelockOption);
}
Возможно, для опытным разработчиков она окажется простой, но для других - интересным опытом.
Обратите внимание, что в параметрах функции 5 аргументов, 3 из которых обозначены просто как тип uint256 без переменных.
Далее, я не совсем разобрался, как называется это действие, но мы создаем некие поинтеры на другие функции в контракте и кладем их в переменные _s1, _s2, _b, _lp.
_stakeS1Citizen, _stakeS2Citizen, _stakeBytes, _stakeLP - да, это все функции в данном контракте, например:
function _stakeS1Citizen (uint256 _timelock) private {}
Также нужно учесть, что в данном примере, один из аргументов в функции, assetType - это enum. Вот как он выглядит:
enum AssetType {S1_CITIZEN, S2_CITIZEN, BYTES, LP}
Т.е. вы поняли, что происходит в функции?
Мы создали поинтеры на другие функции в контракте, а затем через assembler прогнались по enum и переопределили в поинтер _stake необходимую для нас опцию!
Другими словами, исполнение данной функции продолжится уже в другой функции, учитывая значения AssetType!
Дальше еще круче!
Помните те 3 безыменных аргумента uint256 в функции?
Они передаются в последующие функции, как calldata! Например, одна из функция такая:
function _stakeBytes (uint256) ) private {
uint256 amount;
uint256 citizenId;
uint256 seasonId;
assembly{
amount := calldataload(0x44)
citizenId := calldataload(0x64)
seasonId := calldataload(0x84)
}
}
Эта calldata из безымянных uint256 передается в другие функции и расшифровывается в assembly. И что самое интересное, во всех 4 функциях _stakeS1Citizen, _stakeS2Citizen, _stakeBytes, _stakeLP - расшифровываются разные calldata в зависимости от полученных в главное функции stake!
В общем, меня впечатлил данный контракт! Надеюсь и вам он понравится.
#function
Post #749
568
- 👍 7
- 🤯 1