multiDelegatecall - demonoved/A-trial-run-in-solidity GitHub Wiki
// SPDX-License-Identifier: MIT
// compiler version must be greater than or equal to 0.8.20 and less than 0.9.0
pragma solidity ^0.8.23;
//пример вызова нескольких функций с помощью одной транзакции
contract MultiDelegatecall{
error DelegatecallFailed();
function multiDelegatecall(
bytes[] memory data
) external payable returns (bytes[] memory results){
results = new bytes[] (data.length);
for (uint i; i < data.length; i++){
(bool ok, bytes memory res) = address(this).delegatecall(data[i]);
if (!ok){
revert DelegatecallFailed();
}
results[i] = res;
}
}
}
//alice -> multicall --- call ---> test(msg.sender = multicall)
//alice -> test --- delegatecall ---> test(msg.sender = alice)
contract TestMultiDelegatecall is MultiDelegatecall{
event Log(address caller, string func, uint i);
function func1(uint x, uint y) external{
//msg.sender = alice
emit Log(msg.sender, "func1", x + y);
}
function func2() external returns (uint){
//msg.sender = alice
emit Log(msg.sender, "func2", 2);
return 111;
}
mapping (address => uint) public balanceOf;
//Не безопасный код при использовании в сочетании с multidelegatecall
//пользователь может минтить несколько раз по цене msg.value
function mint() external payable{
balanceOf[msg.sender] += msg.value;
}
}
contract Helper {
function getFunc1Data(uint x, uint y) external pure returns (bytes memory){
return abi.encodeWithSelector(TestMultiDelegatecall.func1.selector, x, y);
}
function getFunc2Data()external pure returns (bytes memory){
return abi.encodeWithSelector(TestMultiDelegatecall.func2.selector);
}
function getMintData() external pure returns (bytes memory){
return abi.encodeWithSelector(TestMultiDelegatecall.mint.selector);
}
}
Данный код, написанный на языке программирования Solidity для создания смарт-контрактов на блокчейне Ethereum, показывает реализацию паттерна, который позволяет вызывать несколько функций в одной транзакции. Вот разбор основных компонентов и их предназначение:
-
MultiDelegatecall
: Это базовый контракт, который имеет функциюmultiDelegatecall
, позволяющую выполнить несколько действий (функций), каждое из которых передается как элемент массива байтdata
. Он используетdelegatecall
для вызова каждой функции, которая должна быть уже определена в контракте (или его сабконтрактах). Используется в механизме управления контрактом, позволяя агрегировать вызовы и экономить на транзакциях. -
DelegatecallFailed
: Это пользовательская ошибка, которая используется внутриmultiDelegatecall
для обработки ситуации, когдаdelegatecall
не выполняется успешно. -
TestMultiDelegatecall
: Это производный контракт, который наследуетMultiDelegatecall
и добавляет логику для трех функций:func1
,func2
, иmint
.func1
иfunc2
просто логируют вызов, указываяmsg.sender
(адрес, который инициировал транзакцию) и другие параметры вызова.mint
увеличивает баланс отправителя наmsg.value
, количество эфирных монет, отправленных вместе с вызовом функции. Это указывает на возможную уязвимость, поскольку с помощьюmultiDelegatecall
этот контракт можно вызывать несколько раз за одну транзакцию, позволяя одному пользователю многократно увеличить баланс только за один раз отправив оплату (msg.value
).
-
Helper
: Это вспомогательный контракт, который предоставляет удобный способ для генерации данных, необходимых для вызова функцийfunc1
,func2
иmint
черезmultiDelegatecall
. ФункцииgetFunc1Data
,getFunc2Data
иgetMintData
возвращают закодированные для вызова данные, соответствующие селекторам функций (идентификаторам).
Использование delegatecall
означает, что вызываемые функции (func1
, func2
и mint
) будут выполняться с контекстом состояния вызывающего контракта (TestMultiDelegatecall
), но с сохранением msg.sender
и msg.value
из первоначального вызова, что позволяет тонко управлять правами и логикой выполнения функций в дочерних и родительских контрактах.
Такая техника может использоваться для различных целей, начиная от простого батчинга операций для экономии на газе (плата за транзакции в сети Ethereum) и заканчивая более сложными механизмами управления и рефакторинга логики смарт-контрактов. Однако она также увеличивает сложность контракта и потенциальные уязвимости, особенно в контексте безопасности делегированных вызовов.