Вчера закончились продажи курса, а значит мы возвращаемся в рабочую колею. И начнем нашу неделю с разговора о модификаторе nodelegatecall.
Модификатор nodelegatecall предотвращает отправку вызовов delegatecall в контракт. Сначала я покажу сам код, а затем мы обсудим, для чего это нужно.
Ниже упрощенный модификатор nodelegatecall, изначально созданный в Uniswap V3 для noDelegateCall:
contract NoDelegateCallExample {
address immutable private originalAddress;
constructor() {
originalAddress = address(this);
}
modifier noDelegateCall() {
require(address(this) == originalAddress, "no delegate call");
_;
}
}Address(this) будет меняться в зависимости от среды выполнения, но originalAddress всегда будет адресом кода, использующего nodelegatecall. Поэтому если другой контракт выполнит delegatecall функции с модификатором noDelegateCall, то address(this) не будет равен originalAddress и транзакция откатится.
Крайне важно, чтобы original address был неизменяемой переменной, иначе контракт, выполняющий delegatecall, мог бы намеренно поместить в этот слот адрес контракта, использующего NoDelegateCall, и обойти оператор require.
Вот, например, как этот модификатор может быть протестирован:
contract NoDelegateCall {
address immutable private originalAddress;
constructor() {
originalAddress = address(this);
}
modifier noDelegateCall() {
require(address(this) == originalAddress, "no delegate call");
_;
}
}
contract A is NoDelegateCall {
uint256 public x;
function increment() noDelegateCall public {
x++;
}
}
contract B {
uint256 public x; // this variable does not increment
function tryDelegatecall(address a) external {
(bool ok, ) = a.delegatecall(
abi.encodeWithSignature("increment()")
);// ignore ok
}
}
Контракт B выполняет delegatecall для A, который использует модификатор nodelegatecall. Хотя транзакция B.tryDelegatecall не откатится, поскольку возвращаемое значение низкоуровневого вызова игнорируется, переменная хранения x не будет увеличена, поскольку транзакция внутри контекста delegatecall вернется.
Зачем вообще использовать nodelegatecall?
Uniswap V2 - один из самых популярных протоколов DeFi. Он столкнулся с конкуренцией со стороны других проектов, которые копировали его исходный код построчно и продвигали новый продукт как альтернативу Uniswap V2, а иногда и стимулировали пользователей, предоставляя airdrop.
Для того чтобы предотвратить это, команда Uniswap лицензировала Uniswap V3 через Business Source License - любой может скопировать код, но он не может использовать его в коммерческих целях до истечения срока действия лицензии в апреле 2023 года.
Однако если кто-то захочет сделать «копию» Uniswap V3, он может просто создать прокси-клона и указать ему на контракты Uniswap V3, а затем продавать этот проект, как альтернативу Uniswap V3. Модификатор nodelegatecall не позволяет этого сделать.
#nodelegatecall