setApprovalForAll — это функция ERC-721 / ERC-1155, которая выдаёт контракту или адресу blanket разрешение передавать каждый NFT или токен определённой коллекции, принадлежащий вашему адресу — настоящие и будущие. Одна подпись, неопределённый scope, без лимита на токен. Паттерн OpenSea "approve один раз, торгуй много раз" зависит от этого; киты Inferno Drainer и Pink Drainer эксплуатируют это. В долларовых терминах это одна из самых дорогих вызовов функций в истории Ethereum.
Зачем существует setApprovalForAll
NFT-маркетплейсам нужна возможность передавать NFT от имени продавцов без требования отдельной approve-транзакции для каждого listing. OpenSea, LooksRare, X2Y2, Blur — все используют setApprovalForAll, чтобы выдавать своему маркетплейс-контракту разрешение перемещать NFT, когда продажи исполняются.
Это удобно для легитимного использования. Это также one-signature катастрофа, если одобренный контракт оказывается вредоносным. В отличие от ERC-20 approval (которые имеют лимит суммы на токен), setApprovalForAll бинарен: да или нет, всё или ничего.
Механика drainer
Фейковая страница mint → пользователь подключает кошелёк → кнопка mint запрашивает setApprovalForAll на самый ценный NFT-контракт в кошельке пользователя → пользователь подписывает без чтения → злоумышленник теперь имеет права передачи для каждого NFT в той коллекции по тому адресу.
Злоумышленник обычно задерживает drain. Иногда они ждут до того, как пользователь добавил больше NFT в кошелёк (большая добыча). Иногда они ждут до рыночного события, снижающего бдительность пользователя. Иногда drain происходит немедленно, если кошелёк содержит time-sensitive активы вроде staked NFT, готовых к разблокировке.
По бухгалтерии Chainalysis, один Inferno Drainer эксфильтровал примерно $80 миллионов в 2023, в первую очередь через подписи setApprovalForAll на Discord-распространяемых фейковых страницах mint.
Как UI кошелька представляет setApprovalForAll
Транзакция читается как "setApprovalForAll" в поле функции, с двумя параметрами: адрес "operator" (контракт, получающий approval) и булево (true для grant, false для revoke). Современные кошельки вроде Rabby и пост-2024 MetaMask отображают это ясно, часто с предупреждением, если адрес operator неизвестен.
Более старая прошивка аппаратного кошелька показывает raw-транзакцию с hex-encoded данными. Прошивка Ledger Nano S Plus до 2024 года не парсила setApprovalForAll осмысленно; Trezor Safe 3 и Safe 5 делают. Если ваш аппаратный кошелёк отображает raw hex для незнакомого вызова, относитесь к этому как к red flag — откажитесь подписывать, обновите прошивку, попробуйте снова.
Что защищает
Три правила, которые работают на практике:
Первое, одобряйте только маркетплейсы, которые вы активно используете. Если вы минтите исключительно на OpenSea, не одобряйте контракт LooksRare. Каждый неиспользуемый approval — это будущая поверхность атаки.
Второе, отзывайте после первого использования незнакомых контрактов. Если вы подключаетесь к новому dApp один раз для airdrop claim, сразу после посетите revoke.cash и удалите approval.
Третье, разделяйте кошельки "trading" и "vault". Держите ценные NFT по адресу, у которого нет approval к чему-либо. Когда хотите листинговать один, переведите на trading-адрес, сделайте listing, затем переведите остатки обратно. Это добавляет трение, но полностью устраняет класс катастрофы setApprovalForAll.
Дополнительное чтение: EIP-2612 Permit, revoke.cash, Фишинг.