:::section{mins=5} Анатомия подписанной инструкции :::
:::lead Нажать «Send» в кошельке не значит отправить деньги. Это значит собрать структуру из девяти полей, посчитать от неё хеш, подписать его приватным ключом и отдать получившийся пакет в сеть. Дальше уже не вы решаете: сеть либо принимает пакет, либо нет. Отозвать подпись после отправки нельзя. :::
:::p{html} Возьмём обычный путь: вы зашли на Uniswap, выбрали обмен 100 USDC на [[gloss:ethereum|ETH]], нажали Confirm в Rabby. Кошелёк за вас собрал девять полей: что, кому, с какими параметрами, в какой сети, на каких условиях по газу. Подпись закрепляет ровно эти девять, в том виде, в котором они есть в момент подписи. Если декодер кошелька показал осмысленную картину, байты совпадают с намерением. Если декодер показал «unknown function» или сырые байты, подпись всё равно закрепится, просто что именно она закрепила, вы не увидите. Из этого следует весь дальнейший разбор полей: подпись фиксирует конкретные байты, а не действие, которое вы вроде бы одобрили. :::
:::p{html} [[gloss:ethereum|Ethereum]]-транзакция состоит из девяти полей. Они делятся на три группы по смыслу: что и кому, сколько вы за это платите, куда и в каком порядке. Подпись фиксирует все девять одновременно – поэтому имеет значение каждое из них. :::
:::p{html}
| Группа | Поле | Что определяет | Пример |
|---|---|---|---|
| Что и кому | from | Отправитель (ваш адрес) | 0xd8dA…6045 |
| to | Получатель: адрес или контракт | 0x7a25…488D | |
| value | Сколько [[gloss:ethereum|ETH]] отдаёте | 0.05 [[gloss:ethereum|ETH]] | |
| data | Вызов функции контракта | 0x38ed1739… | |
| Сколько готовы платить | [[gloss:gas|gas]] limit | Потолок газа на эту [[gloss:transaction|TX]] | 180 000 |
| maxFeePerGas | Потолок цены за газ | 5 Gwei | |
| maxPriorityFee | Чаевые валидатору | 0.5 Gwei | |
| Очередь и сеть | [[gloss:nonce|nonce]] | Номер [[gloss:transaction|TX]] от вашего адреса | 42 |
| [[gloss:chainid|chainId]] | Идентификатор сети | 1 ([[gloss:ethereum|Ethereum]]) |
:::p{html} Из этих девяти полей самое непрозрачное – data. На простом переводе [[gloss:ethereum|ETH]] оно пустое и нет вопросов. На любом взаимодействии с контрактом (swap, approve, deposit, mint) туда уходит сжатая в шестнадцатеричный код инструкция: «вызови вот эту функцию с такими аргументами». Без декодера эти байты выглядят как мусор вроде 0x38ed1739000000000000000000000000a0b86991…. Кошельки уровня Rabby или обновлённый MetaMask разбирают поле и показывают по-человечески: «вы вызываете swapExactTokensForTokens на Uniswap V2 Router, обмениваете 100 USDC на [[gloss:ethereum|ETH]] с минимумом 0.045 [[gloss:ethereum|ETH]]». Если такого разбора нет, вы подписываете байты, смысла которых не видите. Почти все дорогие истории потерь последних двух лет начинаются с этой строки; крупнейший пример, февральский хак Bybit на 1,5 миллиарда, разберём детально в §4. :::
:::p{html} Подпись это не печать одобрения «в целом». Кошелёк берёт все девять полей, сворачивает их в один хеш функцией keccak256 и подписывает уже этот хеш приватным ключом по схеме [[gloss:ecdsa|ECDSA]]. Любое изменение хоть одного поля меняет хеш, и старая подпись становится недействительной. Отсюда два свойства, которые ломают интуицию. Первое называется атомарностью: транзакция исполняется целиком или не исполняется вообще. Если на любом шаге контракт упирается в ошибку, всё откатывается, но газ за попытку списан. Это и есть revert: вы заплатили за вычисления и ничего не получили. Второе называется необратимостью: подписанную транзакцию нельзя отозвать, как банковский перевод. Можно только обогнать её другой транзакцией с тем же [[gloss:nonce|nonce]] и более высокой ценой газа (это называется speed-up или cancel, разберём в §6). :::
:::callout{icon=💡} Международная транспортная накладная на складе таможенного брокера. На бланке девять граф: кто отправляет, кому, что внутри, объявленная стоимость, какой логистический тариф, какая страна назначения, какой по счёту контейнер от этого клиента. Отправитель подписывает накладную внизу, и эта подпись закрепляет именно те графы, которые в ней сейчас записаны. Если кто-то после подписи дорисует ноль к стоимости или поменяет страну, штамп брокера на накладной перестанет совпадать с её содержимым, и груз вернут на склад. И ещё одно важное свойство: «слегка отправить» нельзя. Контейнер либо ушёл в порт по плану, либо застрял в очереди, либо его развернули, но сбор за оформление вы уже заплатили. :::
:::keypoints{label="Ключевые моменты"}
- Поле value измеряет только нативный токен сети – [[gloss:ethereum|ETH]] для [[gloss:ethereum|Ethereum]], MATIC для Polygon, ничего больше. На переводе $10 000 в USDC value будет ровно 0, а сама сумма уйдёт через data, в виде вызова transfer() на контракте USDC. Поэтому транзакция «на ноль [[gloss:ethereum|ETH]]» на Etherscan может оказаться многомиллионной – смотреть надо на data, не на value.
- [[gloss:ecdsa|ECDSA]]-подпись математически доказывает, что транзакция исходит от владельца адреса в from. Что в to стоит «правильный» адрес – подпись не утверждает. Криптография не знает, кому вы собирались отправлять; она только подтверждает, что отправляете именно вы. Опечатка в адресе, подмена через clipboard-malware, address poisoning – всё это попадает в подпись как ваше явное согласие.
- Установить [[gloss:gas|gas]] limit 500 000 вместо 200 000 не сделает транзакцию дороже. Сеть спишет ровно тот газ, который реально потребовался для исполнения (gasUsed), а лимит – это страховка от бесконечного цикла. Цена газа отдельная: maxFeePerGas и maxPriorityFee. Путаница между «лимитом газа» и «ценой газа» – самая частая причина переплаты, когда пользователь крутит не ту ручку.
- Кошелёк подписывает транзакцию на состоянии сети сейчас: курс, цены пулов, baseFee. Но в блок она попадает через 12 секунд или позже, и состояние к тому моменту другое. За эту дельту вы платите slippage, в неё ныряют sandwich-боты (см. §3), из-за неё транзакция падает в revert по out-of-[[gloss:gas|gas]], когда подросла цена. Подпись валидна, мир изменился. :::
:::details{badge=Глубже title="ABI и function selector: как имя функции превращается в 4 байта"} :::p{html} Когда вы вызываете функцию контракта, кошелёк собирает поле data по правилам ABI (Application Binary Interface), стандарта, который описывает, как имена функций и значения параметров превращаются в байты. Первые 4 байта data называются function selector. Это первые 4 байта от keccak256-хеша сигнатуры функции. Сигнатура состоит из имени функции и типов аргументов без пробелов: swapExactTokensForTokens(uint256,uint256,address[],address,uint256). Считаем keccak256, берём первые 4 байта, получаем 0x38ed1739. Дальше идут сами параметры, каждый длиной ровно 32 байта (типы либо упаковываются в эту длину, либо ссылаются на хвост для динамических массивов). :::
:::p{html} Etherscan, Tenderly и Rabby хранят базы известных селекторов. Видя 0x38ed1739, они узнают функцию Uniswap V2 Router и расшифровывают её аргументы. Если селектор неизвестен, кошелёк покажет сырые байты. Это первый красный флаг: либо контракт совсем новый, либо его пытаются скрыть от декодеров. ::: :::
:::details{badge=Глубже title="Эволюция формата транзакций: от legacy до EIP-7702"} :::p{html}
- Type 0 (legacy) – the original format with a single gasPrice field; still valid.
- Type 1 ([[gloss:eip|EIP]]-2930) – adds an accessList, a prepayment for touching rarely-accessed storage slots; rarely used in practice by [[gloss:wallet|wallets]].
- Type 2 ([[gloss:eip|EIP]]-1559) – the modern standard with maxFeePerGas and maxPriorityFee described above; what MetaMask and Rabby send by default.
- Type 3 ([[gloss:eip-4844|EIP-4844]]) – [[gloss:blob|blob]] [[gloss:transaction|transactions]], activated in [[gloss:dencun|Dencun]] in March 2024. End users never see these; they are an [[gloss:layer 2|L2]] mechanism for posting batch data to [[gloss:l1|L1]] cheaply.
- Type 4 ([[gloss:eip-7702|EIP-7702]] SetCode) – new in [[gloss:pectra|Pectra]], May 2025. Authorizes an [[gloss:eoa|EOA]] to temporarily execute smart-contract code within a single [[gloss:transaction|transaction]]. This is the mechanism that lets a vanilla MetaMask act as a smart account without migrating the [[gloss:wallet|wallet]] (see 3.2 §3).
:::p{html} [[gloss:eip|EIP]]-2718 ввёл «конверт» для разных типов транзакций. Каждая [[gloss:transaction|TX]] начинается с байта-типа, и парсер знает, как читать дальше: :::
:::p{html}
- Type 0 (legacy) – изначальный формат, единое поле gasPrice, до сих пор валиден.
- Type 1 ([[gloss:eip|EIP]]-2930) – добавляет accessList, предоплату за обращение к редким слотам storage; на практике почти не используется в кошельках.
- Type 2 ([[gloss:eip|EIP]]-1559) – современный стандарт с maxFeePerGas и maxPriorityFee, описан выше; то, что отправляет MetaMask и Rabby по умолчанию.
- Type 3 ([[gloss:eip-4844|EIP-4844]]) – [[gloss:blob|blob]]-транзакции, активированы в [[gloss:dencun|Dencun]] в марте 2024. На пользовательском уровне их никто не видит, это инструмент [[gloss:layer 2|L2]] для дешёвой публикации данных на [[gloss:l1|L1]].
- Type 4 ([[gloss:eip-7702|EIP-7702]] SetCode) – новинка [[gloss:pectra|Pectra]], май 2025. Авторизует [[gloss:eoa|EOA]] временно исполнять код смарт-контракта в рамках одной транзакции. Это и есть механизм, который превращает MetaMask в smart account без миграции кошелька (см. 3.2 §3).
:::p{html} Все пять типов сейчас живут в сети одновременно. Кошелёк выбирает тип по контексту, и обычно вы об этом не задумываетесь. Но когда транзакция «не пролезает» через старый RPC или эксплорер показывает «неизвестный тип» – корень обычно тут. ::: :::
:::quiz{id=q-3-3-s1 correct=1 explanation="Верно. Подпись закрепляет все девять полей, включая [[gloss:chainid|chainId]]. Если изменить хоть один байт, хеш будет другим, и старая подпись для нового хеша невалидна. Это и есть защита от replay-атак, введённая в [[gloss:eip|EIP]]-155 ещё в 2016 году. Вам придётся переключить кошелёк на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] и подписать заново: с новым [[gloss:chainid|chainId]], новым [[gloss:nonce|nonce]] (для основной сети у вашего адреса свой счётчик) и текущим baseFee основной сети."} Вы подписали транзакцию для отправки 0.1 [[gloss:ethereum|ETH]] на адрес друга, но забыли: кошелёк был переключён на Arbitrum, а друг ждёт [[gloss:ethereum|ETH]] в основной сети. Можно ли переотправить эту же подпись в [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]], чтобы не подписывать заново?
- Да, подпись валидна для любой сети с тем же адресом получателя
- Нет, потому что [[gloss:chainid|chainId]] входит в подписываемый хеш, и для другой сети нужна новая подпись
- Да, но только если [[gloss:nonce|nonce]] совпадает на обеих сетях
- Нет, потому что на Arbitrum другой формат подписи [[gloss:ecdsa|ECDSA]] :::
:::section{mins=4} Цена включения: gas :::
:::lead Газ это не доллары и не [[gloss:ethereum|ETH]]. Это единица работы, которую выполняют все компьютеры сети, исполняя вашу транзакцию. Сколько работы – определяет инструкция; сколько стоит единица работы – определяет аукцион блочного пространства прямо сейчас. В мае 2026 типичный swap на Uniswap обходится в 13 центов. В мае 2022 это же действие стоило 50-100 долларов. О том, как пользоваться этой механикой сегодня, эта секция. :::
:::p{html} Механику газа (опкоды, [[gloss:eip|EIP]]-1559, burn) подробно разбирали в 2.2 §3. Здесь о другом: когда вы нажимаете «Confirm», кошелёк показывает примерную стоимость и три поля под капотом: gasLimit, maxFeePerGas, maxPriorityFee. Что в этом окне реально означает каждое число, и почему «оценка $0.15» это не цена, а прогноз – тема §2. На сегодня baseFee на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] колеблется в районе 0,4 Gwei, в 50-100 раз ниже пиков 2021-2022. [[gloss:ethereum|ETH]] стоит около 2 120 долларов. Из этих чисел складываются конкретные цифры в окне. :::
:::p{html}
| Действие | Газ | [[gloss:ethereum|Ethereum]] [[gloss:l1|L1]] | Base [[gloss:layer 2|L2]] |
|---|---|---|---|
| Перевод [[gloss:ethereum|ETH]] | 21 000 | ~$0.02 | <$0.001 |
| Перевод USDC | ~65 000 | ~$0.06 | <$0.001 |
| Approve | ~46 000 | ~$0.04 | <$0.001 |
| Swap Uniswap V2 | ~150 000 | ~$0.13 | ~$0.001 |
| Депозит в Aave | ~250 000 | ~$0.21 | ~$0.002 |
| Mint [[gloss:nft|NFT]] | ~100 000 | ~$0.08 | ~$0.001 |
:::p{html} Окно подтверждения показывает «estimated fee ~$0.13», но реальная плата считается уже после включения в блок по формуле (baseFee + priorityFee) × gasUsed. Если ваш maxFeePerGas выше итоговой цены, разница не списывается и не сжигается – она просто не нужна. Если ниже, транзакция остаётся ждать в [[gloss:mempool|mempool]], пока baseFee не упадёт до приемлемого уровня (см. §6 про stuck [[gloss:transaction|TX]]). А если gasUsed к моменту исполнения окажется больше gasLimit, который вы задали, – транзакция уйдёт в out-of-[[gloss:gas|gas]] revert, и сеть всё равно спишет газ за выполненные шаги. Estimate в кошельке это симуляция на состоянии «прямо сейчас», и через 12 секунд состояние уже другое (см. §1 KP4). :::
:::callout{icon=💡} maxFeePerGas работает как лимитный ордер на бирже. Вы говорите брокеру: «купи [[gloss:ethereum|ETH]] по цене не выше 2100 долларов». Если рынок открывается на 2050 – вы купите по 2050, не по 2100. Разница не списывается с вашего счёта и никуда не девается, это просто граница, за которую вы не пойдёте. Если рынок открывается на 2200 – ордер не исполнится, вы будете ждать, пока цена не вернётся в ваш диапазон. То же самое с газом: кошелёк выставляет потолок с запасом 2-3 раза от текущего baseFee, и это нормально. Запас не «жжётся», он просто означает «я готов заплатить столько, если придётся, но скорее всего не придётся». А если транзакция «зависла», возможно baseFee резко подскочил и упёрся в ваш потолок – точно так же, как лимитный ордер не исполняется, когда рынок ушёл. :::
:::keypoints{label="Ключевые моменты"}
- Частая ошибка чтения: gasLimit 300 000 не значит «больше этой суммы в долларах с меня не спишут». gasLimit это потолок объёма работы, а не ваших расходов. Если контракт израсходовал 180 000 газа, вы заплатите за 180 000 × текущая цена – не за 300 000. Лимит существует, чтобы защитить вас от бесконечного цикла или контракта, который оказался дороже ожиданий. Поставить лимит слишком низко – получить out-of-[[gloss:gas|gas]] revert; поставить с запасом – безопасно и ничего не стоит сверху.
- «Cancel» в кошельке это не отзыв подписи. Это новая транзакция с тем же [[gloss:nonce|nonce]] и более высокой ценой газа, отправленная чтобы попасть в блок вместо исходной. Эта вторая транзакция тоже идёт в сеть и тратит газ – минимум 21 000 за пустой перевод, по текущей цене. Speed-up работает так же: это ещё одна [[gloss:transaction|TX]] с ещё одной комиссией. Передумать в [[gloss:ethereum|Ethereum]] бесплатно нельзя – детальнее в §6.
- baseFee вы не контролируете – он рассчитывается алгоритмом. gasUsed тоже не контролируете – он определён кодом контракта. Влиять на скорость включения вы можете только через priorityFee (чаевые валидатору). Для обычной транзакции хватает 0,01-0,05 Gwei, для срочной 0,5-1 Gwei, для критической 2-5 Gwei. Большинство переплат происходит, когда пользователь сильно завышает чаевые без необходимости.
- Все три параметра (gasLimit, maxFeePerGas, maxPriorityFee) на Arbitrum, Base, Optimism работают так же, как на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]]. Но baseFee там измеряется не в десятых Gwei, а в десятитысячных – типичная транзакция стоит не центы, а десятые доли центов. На пользовательском уровне разница в том, что страховка-запас в 2-3× ничего не стоит, и переплата на [[gloss:layer 2|L2]] практически невозможна. Детально про [[gloss:layer 2|L2]]-цены в §7. :::
:::details{badge=Практика title="Как настроить priorityFee и не переплачивать"} :::p{html}
- Non-urgent [[gloss:transaction|transaction]] (transfer to a friend, a swap not tied to a pump): 0.01–0.05 Gwei. Pennies.
- Urgent (catching arbitrage, small-[[gloss:mev|MEV]] races): 0.5–1 Gwei.
- Critical (liquidating a position, signing during a volatile [[gloss:move|move]]): 2–5 Gwei.
:::p{html}
- Etherscan [[gloss:gas|Gas]] Tracker (etherscan.io/gastracker) – three tiers (slow / standard / fast) with rough latency and price
- ultrasound.money – current baseFee, live burn rate
- Blocknative [[gloss:gas|Gas]] Estimator – probability of inclusion in the next 1–3 [[gloss:block|blocks]]
- The [[gloss:wallet|wallet]] itself (MetaMask, Rabby) – three buttons: aggressive / market / low
:::p{html} Чаевые валидатору это единственная ручка, которой вы реально влияете на скорость включения в блок. Точные ориентиры на май 2026: :::
:::p{html}
- Не-срочная транзакция (перевод другу, обычный swap не в момент пампа): 0,01-0,05 Gwei. Это копейки.
- Срочная (поймать арбитраж, мини-[[gloss:mev|MEV]]-конкуренция): 0,5-1 Gwei.
- Критическая (ликвидация позиции, подпись во время волатильного движения): 2-5 Gwei.
:::p{html} Как смотреть текущие цены в реальном времени: :::
:::p{html}
- Etherscan [[gloss:gas|Gas]] Tracker (etherscan.io/gastracker) – три варианта (slow / standard / fast) с примерной задержкой и ценой
- ultrasound.money – текущий baseFee, скорость burn в реальном времени
- Blocknative [[gloss:gas|Gas]] Estimator – вероятность включения в следующих 1-3 блоках
- Сам кошелёк (MetaMask, Rabby) – три кнопки aggressive / market / low
:::p{html} Типичная страховка от скачка: maxFeePerGas = baseFee × 2 + priorityFee. Если baseFee внезапно вырастет в момент включения, у вас есть запас. Если не вырастет, спишется ровно baseFee + priorityFee, разница не сгорит. Запас на потолке это страховка, а не предоплата. ::: :::
:::quiz{id=q-3-3-s2 correct=2 explanation="Верно. maxFeePerGas это потолок, а не цена. Реальная плата считается как (baseFee + priorityFee) × gasUsed. Если ваш потолок выше, разница не списывается и не сжигается. Если ниже, транзакция ждёт. Estimate в кошельке учитывает текущий baseFee, ваш priorityFee и предполагаемый gasUsed: он близок к реальности, но не точен, потому что состояние сети к моменту исполнения немного изменится. Запас на maxFeePerGas это страховка, а не предоплата."} Вы видите в кошельке «estimated fee ~$0.15», ставите maxFeePerGas с двукратным запасом от текущего baseFee, и транзакция проходит без задержки. Сколько вы реально заплатили?
- Около $0.30, потому что протокол списывает ровно maxFeePerGas, который ты установил с двукратным запасом
- Ровно $0.15, потому что протокол гарантирует списание суммы, указанной в оценке кошелька
- Что-то около $0.15, плюс-минус – реально списывается (baseFee + priorityFee) × gasUsed, потолок не списывается
- Ровно maxFeePerGas × gasLimit, то есть максимально возможное значение :::
:::section{mins=6} Mempool и MEV :::
:::lead За последний год пользователи DEX потеряли около 60 миллионов долларов на одной разновидности атаки, о существовании которой большинство не знает. Атака не взлом, не фишинг и не баг кошелька. Это нормальная работа [[gloss:ethereum|Ethereum]], помноженная на ботов, которые читают вашу транзакцию до того, как она попадёт в блок. Самый громкий кейс марта 2025: пользователь обменивал 220 тысяч USDC на USDT и получил 5 тысяч долларов. 98% сделки забрала автоматизированная программа за один блок. :::
:::p{html} Когда вы нажимаете Confirm и кошелёк отправляет подписанную транзакцию в сеть, она не идёт прямо к валидатору. Она попадает на ваш RPC-узел (см. 1.2 §6), который передаёт её другим нодам по P2P-протоколу. Эта стадия называется [[gloss:mempool|mempool]] – общее «помещение ожидания», где транзакции лежат, пока валидатор не выберет их в следующий блок. Ключевая деталь: [[gloss:mempool|mempool]] публичный. Любой, кто запустил [[gloss:ethereum|Ethereum]]-ноду или подключился к сервису вроде Blocknative, видит вашу транзакцию целиком – поля to, data, value, maxFeePerGas – за секунды до её исполнения. Это не утечка и не баг. Это базовый дизайн P2P-сети: чем быстрее транзакция распространяется, тем быстрее попадает в блок. :::
:::p{html} Допустим, вы делаете swap 5000 USDC на [[gloss:ethereum|ETH]] в Uniswap V2 со slippage tolerance 5%. Бот, читающий [[gloss:mempool|mempool]], видит вашу транзакцию и симулирует её на собственном узле за 50 миллисекунд. Он понимает: «эта покупка двинет цену [[gloss:ethereum|ETH]] в пуле на 0,3% вверх». Тогда бот отправляет две свои транзакции с более высоким priorityFee: первая (front-run) до вашей – покупает [[gloss:ethereum|ETH]] дёшево; вторая (back-run) после вашей – продаёт тот же [[gloss:ethereum|ETH]] по поднятой вами же цене. Валидатор упорядочивает блок по чаевым, поэтому транзакции попадают в блок в таком порядке: покупка бота, ваш swap по уже сдвинутой цене (получаете меньше [[gloss:ethereum|ETH]], чем ожидали), продажа бота с прибылью. Конкретный кейс 12 марта 2025: пользователь отправил swap 220 764 USDC → USDT, его slippage был выкручен на максимум, бот извлёк всё что мог – пользователь получил 5 271 USDT. 98% сделки забрала пара front-run + back-run. :::
:::p{html} В 2023-2024 этим жил рынок searchers – независимые операторы ботов, охотящиеся за прибылью в [[gloss:mempool|mempool]]. Сегодня ~70% всех сэндвичей контролирует один игрок, известный как jaredfromsubway.[[gloss:ethereum|eth]]: бот, обработавший к 2025 году более 3 миллионов транзакций и заработавший $40 миллионов с 2023 года. Но не он один – это целая пирамида ролей: searchers ищут возможность, builders собирают блоки оптимально для прибыли, relays передают блоки валидаторам через [[gloss:mev|MEV]]-Boost, proposer (валидатор) включает блок и получает свою долю. Сейчас ~90% блоков на [[gloss:ethereum|Ethereum]] собираются через эту систему. Это не «злодеи нарушают правила», это легальная финансовая инфраструктура, которая существует, потому что валидаторам выгодно отдать сборку профессионалам за процент. :::
:::p{html} Главная защита одна: не отправлять транзакцию в публичный [[gloss:mempool|mempool]]. Есть три практических способа: :::
:::p{html}
- [[gloss:flashbots|Flashbots]] Protect – RPC-эндпоинт (https://rpc.[[gloss:flashbots|flashbots]].net), который кладёт вашу транзакцию в приватный пул и сразу отдаёт её builder'у для включения в блок без бродкаста. Боты её не видят. Бесплатно, добавляется в кошелёк как обычная сеть.
- [[gloss:mev|MEV]] Blocker (https://rpc.mevblocker.io) – то же самое, что [[gloss:flashbots|Flashbots]] Protect, плюс возвращает 90% извлечённого [[gloss:mev|MEV]] пользователю как kickback. На практике это превращает любые «случайные» [[gloss:mev|MEV]] в небольшую прибыль для вас.
- CowSwap, 1inch Fusion – RFQ-системы: вы не отправляете обычный swap, вы публикуете «намерение» (intent), а профессиональные решатели (solvers) конкурируют за то, чтобы исполнить вашу сделку. Сэндвичу здесь некуда вставиться.
:::p{html} Параллельно к любому из трёх работает slippage tolerance – это бюджет, который вы даёте боту. 0,5% защищает гораздо лучше, чем 5%. Многие кошельки по умолчанию ставят 1-3% – переопределяйте вручную при крупных суммах. :::
:::callout{icon=💡} Покерный стол, где у вас на руках обычные карты, а у троих профи за столом – рентгеновские очки. Вы делаете ставку: они уже точно знают, что у вас на руках, и видят вашу ставку за секунду до того, как она ляжет на стол. У одного из них есть право поставить раньше вас (заплатил больше за вход), он мгновенно повышает ставку. Когда наконец доходит ваш ход, цена банка уже сдвинулась. Сразу после вашего хода тот же профи фиксирует прибыль. Вы технически играете по правилам – никто карты не подменил. Вас просто переиграли через информационное преимущество, которое для других игроков совершенно легально. Единственный способ играть нормально – найти стол, где карты непрозрачные (это и есть приватные RPC), или стол с другими правилами, где порядок ходов определяется не быстротой реакции (это RFQ-системы вроде CowSwap). :::
:::keypoints{label="Ключевые моменты"}
- MetaMask, Rabby, Trust – все по умолчанию отправляют транзакцию в публичный [[gloss:mempool|mempool]], потому что это распространяет её быстрее всего. Это не «кошелёк продаёт ваши данные», это так работает P2P-сеть [[gloss:ethereum|Ethereum]]: каждая нода передаёт ваши байты дальше, пока их не увидят все. Чтобы стать невидимым для ботов, нужно явно переключить кошелёк на приватный RPC ([[gloss:flashbots|Flashbots]] Protect, [[gloss:mev|MEV]] Blocker).
- Большинство пользователей читают «slippage 5%» как «я согласен потерять до 5% из-за рыночного движения». Реальный смысл другой: «бот может извлечь из меня до 5%, если успеет». При 5% slippage бот заберёт у вас ровно 5%, а не «небольшое движение цены». Поэтому на крупных swap всегда выставляйте 0,1-0,5% вручную, не надейтесь на «auto» в кошельке.
- Контр-интуитивно: USDC↔USDT swap не должен бы привлекать ботов, цена же не двигается. Но 30% всех сэндвич-атак приходятся как раз на стейбл-пары. Причина в формуле AMM: цена в пуле сдвигается от размера сделки, а не от «настоящей» цены актива. Чем крупнее сумма в стейбле, тем больше бот извлечёт. Тот мартовский кейс на 220 тысяч был именно USDC → USDT, а не каким-нибудь экзотическим обменом.
- Searchers, builders, relays, proposer – это пирамида ролей, которая собирает ~90% блоков [[gloss:ethereum|Ethereum]] через [[gloss:mev|MEV]]-Boost. Это легальные роли, регулируемые рыночной конкуренцией: builders соревнуются за наибольший profit per [[gloss:block|block]], и каждый зарабатывает на своём слое. [[gloss:mev|MEV]] не «вылечат» обновлением протокола, потому что лечить здесь нечего – система работает как задумано. Защита целиком на стороне отправителя транзакции. :::
:::details{badge=Практика title="Как переключиться на Flashbots Protect или MEV Blocker"} :::p{html}
- Open Settings → Networks → Add a network manually
- Network name: [[gloss:flashbots|Flashbots]] Protect (or [[gloss:mev|MEV]] Blocker)
- New RPC URL: https://rpc.[[gloss:flashbots|flashbots]].net (or https://rpc.mevblocker.io)
- [[gloss:chain id|Chain ID]]: 1
- Currency symbol: [[gloss:ethereum|ETH]]
- [[gloss:block|Block]] explorer URL: https://etherscan.io
- Save, switch to the new network in the dropdown
:::p{html} Технически это занимает 60 секунд. В MetaMask: :::
:::p{html}
- Открыть Settings → Networks → Add a network manually
- Network name: [[gloss:flashbots|Flashbots]] Protect (или [[gloss:mev|MEV]] Blocker)
- New RPC URL: https://rpc.[[gloss:flashbots|flashbots]].net (или https://rpc.mevblocker.io)
- [[gloss:chain id|Chain ID]]: 1
- Currency symbol: [[gloss:ethereum|ETH]]
- [[gloss:block|Block]] explorer URL: https://etherscan.io
- Сохранить, переключиться на новую сеть в выпадающем списке
:::p{html} В Rabby то же самое через Settings → RPC, в Trust [[gloss:wallet|Wallet]] через Manage Networks. :::
:::p{html} После переключения каждая ваша транзакция уходит напрямую к builder'у, минуя публичный [[gloss:mempool|mempool]]. Боты её просто не видят. Trade-off: задержка включения может быть на 1-2 блока больше (12-24 секунды), потому что builder ждёт оптимального момента. На практике незаметно. :::
:::p{html} Когда обязательно переключаться: при swap на сумму больше $1000, при подписи [[gloss:permit|permit]]/permit2 (см. §5), при операциях на новых пулах с тонкой ликвидностью. Для микро-транзакций ($20-100) обычный [[gloss:mempool|mempool]] обычно нормально – экономически бот не заинтересуется такой суммой. :::
:::p{html} CowSwap и 1inch Fusion работают иначе – RPC менять не надо, нужно просто использовать их интерфейс вместо обычного Uniswap. Сделки исполняются через RFQ, и боты в этой архитектуре не имеют точки входа. ::: :::
:::details{badge=Глубже title="Пирамида MEV и дело Peraire-Bueno"} :::p{html}
- Searcher – finds [[gloss:mev|MEV]] opportunities in the [[gloss:mempool|mempool]] (sandwiches, arbitrage, liquidations), assembles a bundle of [[gloss:transaction|transactions]], sends it to a builder
- Builder – constructs a full [[gloss:block|block]] out of prioritized bundles from searchers, optimizing for maximum profit per [[gloss:block|block]]
- Relay – the middleman who passes the [[gloss:block|block]] to the proposer, hiding its contents until the signature moment (protection against proposer front-running)
- Proposer ([[gloss:validator|validator]]) – picks the [[gloss:block|block]] with the highest bid from a relay, signs it, takes their share
:::p{html} Сейчас [[gloss:ethereum|Ethereum]] работает через схему Proposer-Builder Separation (PBS), фактически реализованную через [[gloss:mev|MEV]]-Boost. Роли разделены строго: :::
:::p{html}
- Searcher – ищет [[gloss:mev|MEV]]-возможности в [[gloss:mempool|mempool]] (sandwich, арбитраж, ликвидации), собирает bundle транзакций, отправляет builder'у
- Builder – собирает целый блок из приоритезированных bundle'ов от searchers, цель: максимум profit per [[gloss:block|block]]
- Relay – посредник, который передаёт блок proposer'у, скрывая содержимое до момента подписи (защита от proposer-front-running)
- Proposer (валидатор) – выбирает блок с максимальным bid от relay, подписывает, получает свою долю
:::p{html} ~90% блоков [[gloss:ethereum|Ethereum]] проходят через эту цепочку. Прибыль делится между builders (берут себе ~5-10% profit per [[gloss:block|block]]), searchers (получают возможность включить свой bundle), и proposer (получает оплату от builder). :::
:::p{html} Дело Peraire-Bueno (май 2024). Два брата-выпускника MIT – Антон и Джеймс Peraire-Bueno – нашли уязвимость в реализации [[gloss:mev|MEV]]-Boost relay: при определённой комбинации действий можно было раскрыть содержимое блока до подписи proposer'а и переписать транзакции внутри bundle. За один блок (12 секунд) они извлекли около 25 миллионов долларов, переключив транзакции в их пользу. Минюст США в 2024 году выдвинул федеральные обвинения в wire fraud и money laundering. Это первый прецедент, когда [[gloss:mev|MEV]]-extraction квалифицирован как уголовное преступление – но не сам факт [[gloss:mev|MEV]] (он остаётся легальным), а манипуляция инфраструктурой relay. Дело идёт сейчас. :::
:::p{html} Параллельно работает оператор под именем Sandwich The Ripper: ~$20M извлечено через схожие манипуляции relay. Это показывает важное: даже инфраструктура [[gloss:mev|MEV]] сама не «безопасна», она тоже атакуется внутри себя. ::: :::
:::quiz{id=q-3-3-s3 correct=2 explanation="Верно. Бот в публичном [[gloss:mempool|mempool]] видит вашу транзакцию до валидатора и всегда может выставить чаевые выше ваших – так что priorityFee (A) не помогает, ботам этот ресурс доступен в большей степени. Низкий maxFeePerGas (D) только увеличит задержку, незаметность здесь невозможна. Снижение slippage до 0,3% (B) действительно ограничивает прибыль бота – это полезная мера, но она просто сужает атаку, не блокирует её. Единственное, что полностью защищает – отправить транзакцию в обход публичного [[gloss:mempool|mempool]] через [[gloss:flashbots|Flashbots]] Protect или [[gloss:mev|MEV]] Blocker (C). На крупных swap правильная практика – комбинация: переключиться на приватный RPC и выставить slippage 0,3-0,5%."} Вы делаете swap 5000 USDC → [[gloss:ethereum|ETH]] через Uniswap в Rabby. Что лучше всего защищает от sandwich-атаки?
- Поставить высокий priorityFee (5 Gwei), чтобы пройти первым в блоке
- Снизить slippage tolerance до 0,3% вместо стандартных 1%
- Переключить кошелёк на [[gloss:flashbots|Flashbots]] Protect RPC
- Уменьшить maxFeePerGas, чтобы быть незаметным для ботов :::
:::section{mins=7} Что вы подписываете :::
:::lead Подпись в Web3 бывает четырёх разных видов. Один из них устарел и в современных кошельках помечен как опасный. Другой выглядит как обычный текст и используется для логина – он безопасен. Третий это стандартная транзакция, которую мы уже разобрали. Четвёртый – структурированная подпись [[gloss:eip-712|EIP-712]] – выглядит понятно, но именно её научились использовать атакующие. В феврале 2025 этот четвёртый тип стоил бирже Bybit полтора миллиарда долларов за одну подпись. :::
:::p{html} Когда кошелёк всплывает с запросом подтвердить что-то, под капотом у него один из четырёх стандартных методов. Каждый делает что-то своё, и риск у них разный. :::
:::p{html}
- eth_sendTransaction – стандартная транзакция, та самая структура из 9 полей (см. §1). Кошелёк показывает декодированные поля, hardware [[gloss:wallet|wallet]] тоже их декодирует. Это самый понятный для пользователя вид подписи.
- eth_sign – подписывает произвольный 32-байтный хэш без какой-либо структуры. Никто не знает, что внутри этого хэша: возможно, транзакция, возможно – что угодно. Этот метод официально устарел, и современные кошельки (MetaMask, Rabby) отказываются его показывать или предупреждают красной плашкой. Если dApp просит eth_sign – это красный флаг.
- personal_sign – подписывает текстовое сообщение с обязательным префиксом «[[gloss:ethereum|Ethereum]] Signed Message». Префикс гарантирует одно: эту подпись нельзя переиспользовать как подпись транзакции. Используется для логина в dApp («Sign In With [[gloss:ethereum|Ethereum]]»), для off-chain доказательств владения адресом. Hardware-[[gloss:wallet|wallet]] показывает читаемый текст. Это безопасный метод по дизайну.
- eth_signTypedData ([[gloss:eip-712|EIP-712]]) – структурированная подпись. Не текст, а типизированная структура: domain (какой dApp), types (схема полей), message (само содержимое). Если кошелёк знает схему, он показывает читабельно: «[[gloss:permit|Permit]] USDC, 1000 токенов, для контракта 0x...». Используется для [[gloss:permit|permit]]/permit2 (см. §5), для off-chain ордеров на маркетплейсах [[gloss:nft|NFT]], для голосований в DAO. Это удобный стандарт. Но именно его и научились атаковать.
:::p{html} «Blind signing» – это любой случай, когда вы нажимаете Approve, не понимая, что именно ваша подпись закрепляет. На практике это происходит, когда кошелёк или hardware-[[gloss:wallet|wallet]] не смогли декодировать структуру и показывают только хэш вроде 0x3a7f9d...c2. Хэш сам по себе не несёт смысла – это просто отпечаток того, что внутри. Если вы не знаете, что внутри, вы подписываете «что-то» и доверяете тому, кто это «что-то» сформировал. Чаще всего «формировал» – это интерфейс dApp в вашем браузере. Тут вступает в игру цепочка доверия: между сайтом dApp и вашей подписью стоит JavaScript, который выполняется в вашем браузере. Взломанный CDN, любой XSS, подменённый сторонним пакетом скрипт – всё это может изменить то, что отправляется в кошелёк, при том что на экране сайта всё выглядит нормально. Это и есть архитектура Bybit-инцидента. :::
:::p{html} Хрестоматийный пример, где эта цепочка доверия ломается, – взлом Bybit 21 февраля 2025 года: трое подписантов мультисига Safe 3-из-6 одобрили транзакцию, которую интерфейс показывал как рутинный перевод 30 000 [[gloss:ethereum|ETH]], а аппаратные кошельки Ledger отображали только хэш [[gloss:eip-712|EIP-712]] структуры, которую не умели декодировать. Реальная структура содержала delegatecall на контракт атакующих, который переписал Safe implementation и позволил вывести 401 347 [[gloss:ethereum|ETH]] – около 1,46 миллиарда долларов. Пошаговый разбор того, как был подменён Safe UI и что именно видел каждый подписант, лежит в 3.2 §2 – там же симулятор, в котором можно сесть на место подписанта. Для §4 урок узкий: трое компетентных людей с hardware-кошельками подписали [[gloss:eip-712|EIP-712]] вслепую, потому что устройство не смогло её декодировать. Цепочка доверия порвалась на слое между UI dApp и устройством – ровно там, где, по тезису этой секции, она и может порваться. :::
:::p{html} Главный принцип один: никогда не подписывать хэш, который вы не понимаете. На практике это значит несколько конкретных вещей: :::
:::p{html}
- Используйте кошельки с pre-sign simulation. Rabby показывает результат транзакции до подписи: «вы получите X, отдадите Y, баланс контракта Z изменится так-то». MetaMask делает то же через Blockaid. Если симуляция показывает что-то странное или ничего не показывает – отмените.
- Для крупных операций используйте hardware-[[gloss:wallet|wallet]] с large screen. Ledger Stax/Flex и Trezor Safe 5 декодируют [[gloss:eip-712|EIP-712]] независимо от ПК. Если на их экране видно «delegatecall на 0xXXX», а в браузере Safe показал «обычный transfer» – это и есть сигнал не подписывать.
- Для очень крупных операций – Tenderly или другой симулятор. Скопируйте [[gloss:hash|hash]] транзакции в Tenderly Dashboard, посмотрите шаг за шагом, что произойдёт. Это занимает минуту, но даёт независимое отображение.
- Не подписывайте eth_sign никогда. Не «осторожно», а просто никогда. Современные dApps его не используют.
:::p{html} В мае 2026 индустрия работает над стандартом [[gloss:eip|EIP]]-7730 – описание схем [[gloss:eip-712|EIP-712]] в формате, который hardware-кошельки могут читать самостоятельно. Это превратит «обычный» текущий blind signing в clear signing для большинства dApp. Пока стандарт не везде внедрён, ответственность на стороне пользователя. :::
:::callout{icon=💡} Приёмное отделение больницы. Перед вами две стопки бумаг. В первой – подробный формуляр на десяти страницах: что именно будут делать, какой разрез, в какую сторону, под каким наркозом, какие риски. Вы можете прочесть это, переспросить у врача, отказаться, попросить второе мнение. Во второй стопке – только один лист с одной строкой и местом для подписи: «согласен на медицинское вмешательство». Юридически обе подписи одинаковы. Но первая – это informed consent, осознанное согласие. Вторая – blank consent, бланк-согласие. Если в операционной решат сделать что-то другое – вашей подписи на бланке достаточно. Современный Ledger Stax – это та самая «первая стопка», которая показывает подробный формуляр. Хэш на маленьком экране Ledger Nano – это «вторая стопка», на которой написано просто «согласен». Подпись та же. Понимание разное. :::
:::keypoints{label="Ключевые моменты"}
- Off-chain подпись – это не разовое событие. [[gloss:permit|Permit]], подписанный сегодня, без поля deadline или с дедлайном в далёком будущем, остаётся валидным до тех пор, пока вы не отзовёте allowance on-chain (через revoke.cash или напрямую у контракта токена). Атакующему не нужно использовать её сразу – он хранит её и применяет когда выгодно, через недели или месяцы. Интуиция «я подписал давно, наверное уже не действует» неверна: подписи сами по себе не истекают.
- Software-кошелёк (MetaMask, Rabby) – это расширение браузера. Сайт dApp тоже живёт в браузере. Они делят один и тот же процесс, одну и ту же память, один и тот же заражённый браузер, если он заражён. На заражённой машине «кошелёк» и «dApp» – это не две стороны, а один и тот же стек ПО с разными ярлыками. Реальное разделение даёт только hardware-устройство со своим экраном. Для всего остального считайте, что кошелёк и dApp – одна сущность.
- Аккуратно декодированная подпись – это не то же самое, что безопасная. Drainer-наборы в 2025 году пишут технически валидные [[gloss:permit|permit]], которые кошелёк красиво показывает: «approve USDC, $50, для 0xCAFE…». Кошелёк показывает правду – именно это и произойдёт, включая ту часть, где 0xCAFE опустошает ваш USDC. Нужный сигнал – не «расшифровалось ли», а «узнаю ли я spender?» и «value ли это та сумма, что я ожидал, не unlimited?».
- «Безопасный по дизайну» не значит «нулевой риск». Префикс в personal_sign блокирует переиспользование как транзакции, но phishing-сайт, попросивший вас «sign in», заберёт вашу подпись и зайдёт под вашим адресом на любой dApp с авторизацией через Sign In With [[gloss:ethereum|Ethereum]] (SIWE). На Hyperliquid, dYdX и подобных биржах off-chain ордера – тоже сообщения personal_sign: небрежная подпись может выдать торговое разрешение. Читайте текст каждый раз, даже когда формат выглядит безобидно. :::
:::details{badge=Практика title="Чеклист перед подписью EIP-712"} :::p{html}
- Domain.name – the dApp's name in the structure itself. It should match the site you're on. If you're on uniswap.org and domain.name = "SomeOtherDapp", that's a swap.
- Domain.verifyingContract – the address of the contract the signature is for. Open Etherscan, find this address, confirm it's the right contract (for example, a USDC [[gloss:permit|permit]] goes to 0xA0b86...eB48). If the contract is unknown or recently deployed – stop.
- Domain.[[gloss:chainid|chainId]] – the network ID. It should match the [[gloss:wallet|wallet]]'s current network. If you're on [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] ([[gloss:chainid|chainId]] 1) and the signature has [[gloss:chainid|chainId]] 56 (BSC) – the signature is valid for a different network, something's wrong.
- Message – the decoded structure of the deal. What you're signing: [[gloss:permit|permit]] on which token, to which spender, for what amount, by which deadline.
- If decoding is empty or shows a [[gloss:hash|hash]] – cancel, figure out what the dApp is asking for.
:::p{html} Когда кошелёк показывает запрос на подпись «typed data» ([[gloss:eip-712|EIP-712]]), пройдите по списку прежде чем нажать Confirm: :::
:::p{html}
- Domain.name – название dApp в самой структуре. Должно совпадать с тем сайтом, на котором вы находитесь. Если вы на uniswap.org, а domain.name = «SomeOtherDapp», это подмена.
- Domain.verifyingContract – адрес контракта, для которого подписывается. Откройте Etherscan, найдите этот адрес, убедитесь, что это правильный контракт (например, для [[gloss:permit|permit]] USDC это 0xA0b86...eB48). Если контракт неизвестный или создан недавно – стоп.
- Domain.[[gloss:chainid|chainId]] – идентификатор сети. Должен совпадать с текущей сетью кошелька. Если вы на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] ([[gloss:chainid|chainId]] 1), а в подписи [[gloss:chainid|chainId]] 56 (BSC) – подпись валидна для другой сети, что-то не то.
- Message – декодированная структура самой сделки. Что подписываете: [[gloss:permit|permit]] на какой токен, какому spender, на какую сумму, до какого deadline.
- Если декодирование пустое или показывает [[gloss:hash|hash]] – отмените, разберитесь, что просит dApp.
:::p{html} Для крупных операций есть инструмент Tenderly Dashboard (tenderly.co). Скопируйте параметры подписи или [[gloss:hash|hash]] транзакции, прогоните simulation, посмотрите пошаговую трассу исполнения. Это даёт независимый взгляд на то, что произойдёт после подписи – без доверия к фронтенду dApp. :::
:::p{html} Для [[gloss:permit|permit]]-сделок (см. §5) дополнительно проверьте два поля: spender (кто получит право тратить ваши токены) и value (на какую сумму). Бесконечный allowance (число типа 115792089237316195423570985008687907853269984665640564039457584007913129639935) – это типичная подпись drainer'а. Никакой нормальный dApp не просит unlimited [[gloss:permit|permit]] для разовой операции. ::: :::
:::quiz{id=q-3-3-s4 correct=1 explanation="Верно. eth_sign устарел и в 2026 году не должен встречаться вообще. Этот метод подписывает произвольный 32-байтный хэш без декодирования – за этим хэшем может прятаться подпись транзакции, которая выведет ваши средства. personal_sign (A) безопасен по дизайну: префикс «[[gloss:ethereum|Ethereum]] Signed Message» гарантирует, что подпись нельзя переиспользовать как транзакцию. eth_sendTransaction с декодированными полями (C) – стандартная транзакция, риск только в том, что вы сами проверяете её содержимое. eth_signTypedData с видимыми параметрами (D) требует проверки domain и message, но это нормальный современный метод. Появление eth_sign – главный красный флаг: отмените и разберитесь, что dApp пытается сделать."} Вы заходите на [[gloss:defi|DeFi]]-сайт, который просит подписать сообщение. Кошелёк показывает кнопку Confirm и тип запроса. Какой из этих запросов точно требует особой осторожности?
- personal_sign с текстом «Sign in to Aave v3»
- eth_sign, запрос подписать хэш 0x4a8c2d...
- eth_sendTransaction для swap 100 USDC → [[gloss:ethereum|ETH]] с декодированными полями
- eth_signTypedData для [[gloss:permit|permit]] USDC на 50 долларов с видимыми параметрами :::
:::section{mins=8} Approve и Permit :::
:::lead В 2024 году пользователи DEX и [[gloss:defi|DeFi]] потеряли 494 миллиона долларов через одну подпись. Не через взлом, не через утечку сид-фразы – через подпись «approve» или «[[gloss:permit|permit]]», которую сами поставили на phishing-сайте. В 2025 цифра упала до 84 миллионов, и не потому что атака исчезла, а потому что меньше людей сидят в [[gloss:defi|DeFi]] и больше кошельков научились блокировать самые опасные форматы. Подпись разрешает контракту тратить ваши токены. Если разрешение бессрочное – оно лежит навсегда. :::
:::p{html} Когда вы первый раз делаете swap USDC на [[gloss:ethereum|ETH]] в Uniswap, проходят две транзакции, не одна. Сначала approve(spender=Uniswap, amount=X): это запись в storage контракта USDC «контракт по адресу Uniswap имеет право списать с моего адреса X моих USDC». Эта запись называется allowance и она остаётся в storage навсегда, пока вы не сделаете новый approve на 0. Только после первой транзакции возможна вторая, сам swap, во время которого Uniswap вызывает transferFrom и фактически забирает ваши USDC. Большинство пользователей помнит только swap, потому что «получил [[gloss:ethereum|ETH]] сразу после клика». Но первой была approve, и её результат, allowance, лежит навсегда, в большинстве случаев как unlimited (2^256 − 1): кошельки и dApp по умолчанию ставят бесконечную сумму, чтобы не подписывать approve каждый раз. На январь 2026 средний [[gloss:defi|DeFi]]-пользователь имеет 40-60 активных allowances у разных контрактов, забытых, никогда не отозванных. :::
:::p{html} В 2020 году появился стандарт [[gloss:permit|EIP-2612]]: разрешение можно дать не через on-chain транзакцию, а через off-chain подпись. Структура – [[gloss:eip-712|EIP-712]] (см. §4): domain (адрес токена), types (поля [[gloss:permit|Permit]]), message (spender, value, deadline, [[gloss:nonce|nonce]]). Вы подписываете эту структуру у себя в кошельке (без газа), отправляете spender'у, и он сам вызывает [[gloss:permit|permit]] + transferFrom в одной транзакции. На стороне токена это эквивалентно approve + использованию allowance, но без двух отдельных транзакций. USDC поддерживает [[gloss:permit|permit]] с 2020, DAI с самого начала, многие современные ERC-20 тоже. Это удобный паттерн: один клик вместо двух, газ за approve экономится. Но подпись хранится у получателя, и до истечения deadline её можно использовать в любой момент. :::
:::p{html} В августе 2022 Uniswap опубликовал контракт Permit2 по адресу 0x000000000022D473030F116dDEE9F6B43aC78BA3 (адрес как часть бренда, выбран намеренно через CREATE2). Идея простая: вместо того чтобы каждый токен поддерживал свой [[gloss:permit|permit]], есть один универсальный контракт-посредник. Вы один раз делаете обычный approve(Permit2, unlimited) на каждый ваш токен (USDC, USDT, DAI, WBTC, всё). Дальше любая dApp, поддерживающая Permit2, принимает от вас короткие подписи: «разреши Permit2 потратить мои X USDC для контракта Y». Permit2 проверяет подпись и зовёт transferFrom. Никаких отдельных approve больше не нужно ни для какого нового dApp, они все ходят через Permit2. На май 2026 Permit2 стал общим слоем для большинства DEX и агрегаторов: Uniswap V3+, 1inch, Cowswap, NEAR Intents. Удобство огромное. Но есть нюанс: одна подпись Permit2 это разрешение на любой токен, у которого Permit2 имеет allowance. Drainer'ы на это и научились играть: 56,7% всех phishing-атак 2024 года шли через Permit2. :::
:::p{html} К концу 2024 года drainer'ы оформились в полноценную отрасль с собственной экономикой. Главные игроки: :::
:::p{html}
- Inferno Drainer: drainer-as-a-service с 20% комиссии. Около 80 миллионов украдено за 2023-2024. Объявил уход в Q4 2024, продал инфраструктуру Angel Drainer. Check Point Research показывает, что операции продолжаются под новыми именами.
- Pink Drainer: 28% рынка в первой половине 2024, около 55 миллионов. Ушёл в Q2 2024.
- Angel Drainer: 42% рынка в Q1-Q2 2024, унаследовал инфраструктуру Inferno.
- Ace, Vanilla, Cerberus: новые игроки 2025 года. Vanilla заработала 5 миллионов за три недели в августе 2025.
:::p{html} Главный метод оставался прежним: phishing-сайт с фейк-airdrop или фейк-mint просит подпись permit2 «для верификации» или «для claim». Подпись действительно валидна, она и подписала вывод всех токенов, у которых Permit2 имеет allowance. Общие потери 2024 года: 494 миллиона долларов через 332 тысячи кошельков, крупнейшая разовая кража 55,48 миллионов. В 2025 цифра упала до 83,85 миллионов через 106 тысяч жертв. Падение на 83% произошло не потому, что атака исчезла, а потому что MetaMask, Rabby и другие кошельки начали блокировать подозрительные permit2-подписи, dApp-агрегаторы вроде Blockaid отмечают phishing-домены в реальном времени, и пользователи постепенно учатся читать поле spender. :::
:::p{html} После активации [[gloss:pectra|Pectra]] 7 мая 2025 года появился новый формат подписи – [[gloss:eip-7702|EIP-7702]], благодаря которому [[gloss:eoa|EOA]]-адрес может временно стать [[gloss:smart contract|smart contract]]'ом. Хорошие применения: гранулярные разрешения, batch-операции, спонсорство газа. Плохое применение: атакующий подсовывает подпись [[gloss:eip-7702|EIP-7702]], в которой ваш [[gloss:eoa|EOA]] временно «становится» контрактом атакующих, и этот контракт в той же транзакции делает transferFrom по всем токенам, для которых у вас есть какие-либо allowances. Одна подпись, полное опустошение. В августе 2025 произошли два крупных [[gloss:eip-7702|EIP-7702]]-инцидента общей суммой 2,54 миллиона долларов; на январь 2026 это отдельная категория в Scam Sniffer-отчётах, ещё не пиковая, но растущая. :::
:::callout{icon=💡} Подписанный чек, который вы отдали продавцу. approve на конкретную сумму это обычный чек: подпись поставлена, сумма заполнена, продавец может прийти в банк и обналичить. Подпись на пустом бланке (unlimited approve) это разрешение заполнить любую сумму, какую захочет, когда захочет. Бланк лежит в его сейфе, а не у вас. Чтобы отозвать, нужно идти в банк и блокировать счёт; пока не сходили, бланк остаётся валидным. [[gloss:permit|Permit]] – тот же чек, но подписанный удалённо и отправленный получателю по почте, без визита в банк. Permit2 работает как доверенный посредник: вы один раз дали ему генеральную авторизацию на все свои счета, а он раздаёт быстрые суб-чеки разным продавцам по вашим подписям. Drainer-атака это подмена бланка в момент подписи: вы думали, что подписываете чек на 50 долларов для одного магазина, а подписали пустой бланк, который тот же магазин может обналичить во все ваши счета сразу. :::
:::keypoints{label="Ключевые моменты"}
- Если вы один раз подписали approve на vault, который разработчики позже забросили, allowance в storage USDC остаётся активным навсегда. Контракт не «удаляется», когда сайт уходит – он остаётся on-chain и сохраняет все полученные права. Если в его коде позже найдут уязвимость, любой unlimited allowance, который вы когда-либо выдавали этому vault'у, превращается в открытую дверь. Allowance это не активная подписка, это пассивная запись, которая ждёт.
- В отличие от обычного allowance, который живёт навсегда, Permit2 хранит для каждой пары (token, spender) поле expiration: после этой даты разрешение протухает само. Хорошие dApp могли бы ставить expiration на один час или один день. На практике большинство ставит несколько лет вперёд, что эквивалентно бесконечному. Если вы регулярно пользуетесь revoke.cash, обращайте внимание на колонку Expires: что-то вроде «December 31, 2099» это красный флаг, даже если dApp выглядит знакомым.
- Drainer не «взломал Uniswap», не «взломал USDC». Технически контракт USDC работает идеально: пришла валидная подпись на [[gloss:permit|permit]] – контракт уважает её. Защита здесь только на стороне момента подписи. Единственная привычка, которая работает: каждый раз перед нажатием Confirm читать поля spender и value. Если spender не тот, что вы ожидаете, или value – unlimited, не подписывать.
- Это два разных канала, требуют разной защиты. [[gloss:permit|Permit]]-атака разрешает spend конкретного токена; защита – отзыв allowance через revoke.cash, перевод средств на новый адрес. [[gloss:eip-7702|EIP-7702]]-атака превращает ваш [[gloss:eoa|EOA]] в контракт атакующих на одну транзакцию; защита – никогда не подписывать [[gloss:eip-7702|EIP-7702]]-запросы от незнакомых сайтов, проверять, что любая «[[gloss:account abstraction|account abstraction]]»-фича действительно нужна, и помнить, что отозвать сложнее. :::
:::details{badge=Практика title="Как использовать revoke.cash"} :::p{html}
- Go to the site, click Connect [[gloss:wallet|Wallet]]. The connection is read-only; the [[gloss:wallet|wallet]] doesn't sign anything at this stage.
- The site shows three categories: Token approvals (regular ERC-20 allowances), [[gloss:nft|NFT]] approvals (permissions on your [[gloss:nft|NFTs]]), Permit2 allowances (a separate category because Permit2 is a special contract).
- Sorting is by risk: revoke.cash flags unlimited allowances and old/unused dApps in red.
- Each revoke is a button – an on-chain [[gloss:transaction|transaction]] calling approve(spender, 0). [[gloss:gas|Gas]] is normal: roughly 4-10 cents on [[gloss:mainnet|mainnet]], pennies on [[gloss:layer 2|L2]].
- Priority for revocation: unlimited approves to vaults you stopped using; anything Permit2-related for dApps you no longer use; old [[gloss:nft|NFT]] marketplace approvals.
:::p{html} Revoke.cash – главный практический инструмент для управления allowances. Что делать: :::
:::p{html}
- Зайти на сайт, нажать Connect [[gloss:wallet|Wallet]]. Подключение read-only, кошелёк ничего не подписывает на этом этапе.
- Сайт показывает три категории: Token approvals (обычный ERC-20 allowance), [[gloss:nft|NFT]] approvals (разрешения на ваши [[gloss:nft|NFT]]), Permit2 allowances (отдельная категория, потому что Permit2 – особый контракт).
- Сортировка по риску: revoke.cash отмечает unlimited allowances и старые/неиспользуемые dApp красным.
- На каждом revoke нажимаете кнопку – это on-chain транзакция, которая делает approve(spender, 0). Газ обычный, на [[gloss:mainnet|mainnet]] примерно 4-10 центов за revoke, на [[gloss:layer 2|L2]] копейки.
- В первую очередь отзывать: unlimited approve на vault'ы, которые перестали использовать; всё связанное с Permit2 для dApp, которыми не пользуетесь; старые подписки на [[gloss:nft|NFT]]-маркетплейсы.
:::p{html} Регулярный cleanup – каждые 3-6 месяцев. После большого [[gloss:defi|DeFi]]-марафона (например, серия airdrop-claim'ов) – сразу. Стоимость 10-20 revoke'ов на [[gloss:mainnet|mainnet]] – пара долларов. ::: :::
:::details{badge=Глубже title="Permit2 internals и drainer-экосистема"} :::p{html}
- Allowance – for each (owner, token, spender) triple: amount, expiration, [[gloss:nonce|nonce]]. This is what's set by a signature.
- PermitTransferFrom – a one-shot permission for a single operation, no accumulated allowance.
:::p{html} Внутренности Permit2 проще, чем кажется. Контракт хранит две структуры: :::
:::p{html}
- Allowance – для каждой пары (owner, token, spender): amount, expiration, [[gloss:nonce|nonce]]. Это то, что устанавливается через подпись.
- PermitTransferFrom – однократное разрешение для одной операции, без накопления allowance.
:::p{html} Разница важна: первое – постоянное разрешение с истечением; второе – одноразовое. Drainer'ы часто используют второе как «прикрытие», потому что оно выглядит безобиднее («тестовое разрешение для верификации»), но фактически переводит средства сразу. :::
:::p{html} Drainer-экосистема в 2024-2025: после ухода Inferno (Q4 2024) и Pink (Q2 2024) рынок сначала консолидировался у Angel Drainer (60% к концу 2024), затем фрагментировался. На январь 2026 крупные игроки – Angel, Ace, Vanilla, Cerberus. Все работают по drainer-as-a-service модели с 15-20% комиссии. Их «клиенты» – обычные scammer'ы, которым нужна готовая инфраструктура: фишинг-домен с похожим на оригинал интерфейсом, готовый payload, отмывание через миксеры. Это полноценная преступная экономика с конкуренцией, support'ом, маркетингом. :::
:::p{html} Главное изменение 2025 года – общее падение объёмов на 83%, но не из-за «победы над drainer'ами», а из-за двух факторов: общее снижение [[gloss:defi|DeFi]]-активности (особенно после крупных корректировок Q2 2025) и улучшение [[gloss:wallet|wallet]]-защиты. MetaMask интегрировал Blockaid; Rabby по умолчанию блокирует подозрительные Permit2; Phantom и Backpack на [[gloss:solana|Solana]] добавили pre-sign warning. Это смещает атаку в сторону менее технически осведомлённых пользователей и новых форматов вроде [[gloss:eip-7702|EIP-7702]] – те самые 2,54 миллиона в августе 2025. ::: :::
:::quiz{id=q-3-3-s5 correct=2 explanation="Верно. Единственный вариант, который объясняет вывод не только USDC, но и USDT, DAI, WBTC одновременно. Любой swap в Uniswap V3 проходит через Permit2 (контракт 0x000000000022D473030F116dDEE9F6B43aC78BA3), и Uniswap по умолчанию запрашивает approve(Permit2, unlimited) для всех токенов, которые могут понадобиться в сделке. Через год вы об этом не помните, но Permit2 имеет unlimited allowance на ваш USDC и, скорее всего, на [[gloss:ethereum|ETH]]-пары вокруг – USDT и WBTC, если вы их торговали. Подпись «[[gloss:permit|permit]] на 50 долларов USDC» на phishing-сайте на самом деле была подписью на Permit2-операцию, разрешающую вывести любые токены, к которым Permit2 имеет доступ. A не работает: сид-фраза не «утекает через cookies». B – атака только на USDC не объясняет вывод других токенов. D – контракт USDC не взломан, он сработал ровно как должен."} Вы сделали один swap USDC на [[gloss:ethereum|ETH]] в Uniswap V3 в феврале 2024 года. Через год, в феврале 2025, заходите на сайт нового airdrop-claim, который запрашивает «[[gloss:permit|permit]] подпись на 50 долларов USDC» для верификации. Подписываете. На следующий день видите, что выведены не только USDC, но также все ваши USDT, DAI и WBTC. Как это объяснить?
- Phishing-сайт получил доступ к вашей сид-фразе через cookies
- Подпись [[gloss:permit|permit]] на 50 долларов USDC дала атакующему unlimited allowance именно на USDC
- Подпись прошла через Permit2, у которого уже был unlimited allowance на все ваши токены через старый Uniswap V3 swap
- Атакующий взломал контракт USDC и вывел средства всех пользователей :::
:::section{mins=7} Когда транзакция ломается :::
:::lead Каждый, кто хоть раз пользовался [[gloss:defi|DeFi]], видел красную надпись «[[gloss:transaction|Transaction]] failed». Газ списан, действие не выполнилось, кошелёк пишет непонятный код ошибки. Или другой сценарий: [[gloss:transaction|TX]] отправлена, но висит «pending» уже двадцать минут. Или ещё хуже: вы отправили несколько [[gloss:transaction|TX]] подряд, и теперь они все застряли. Каждая из этих ситуаций имеет конкретное название и конкретное решение. :::
:::p{html} Revert происходит так: ваша транзакция попадает в блок (валидатор её взял), [[gloss:evm|EVM]] начинает исполнять инструкции одну за другой, в какой-то момент встречает условие require(), которое не выполняется, или заканчивается газ. [[gloss:evm|EVM]] откатывает все изменения состояния, которые [[gloss:transaction|TX]] успела сделать, и помечает её как «reverted». Деньги не двинулись, [[gloss:nft|NFT]] не minted, swap не произошёл. Но газ, потраченный до точки сбоя, списывается с вашего баланса. Это by design: иначе любой мог бы атаковать сеть бесконечным потоком [[gloss:transaction|TX]], которые падают на первой же инструкции. Газ за неуспешный revert – это плата за CPU валидатора, который всё-таки прогнал ваш код. Типичные причины: out-of-[[gloss:gas|gas]] (вы поставили gasLimit ниже, чем нужно), slippage too high (рынок двинулся), require failed в контракте (например, у вас нет нужного allowance для transferFrom), [[gloss:nonce|nonce]] уже использован. :::
:::p{html} Когда транзакция отправлена, но не появляется в новом блоке через 5-10 минут, возможны три причины: :::
:::p{html}
- maxFeePerGas ниже текущего baseFee. Сеть скакнула выше вашего потолка, валидатор пропустит [[gloss:transaction|TX]] до тех пор, пока цена не упадёт. См. механику в §2.
- [[gloss:nonce|Nonce]]-gap. У вас есть более ранняя [[gloss:transaction|TX]] с меньшим [[gloss:nonce|nonce]], которая ещё не прошла. В [[gloss:ethereum|Ethereum]] [[gloss:nonce|nonce]] последователен: [[gloss:transaction|TX]] #11 не может быть включена, пока не включена #10.
- Перегрузка сети. Бывает редко после [[gloss:fusaka|Fusaka]], но всё равно случается во время событий вроде ажиотажного [[gloss:nft|NFT]]-минта или массовой ликвидации. Просто очередь.
:::p{html} В Etherscan такая [[gloss:transaction|TX]] отображается как «Pending» в верхней панели. На пользовательском уровне это выглядит как «ничего не происходит», но on-chain происходит ровно одно: ваша подпись лежит в [[gloss:mempool|mempool]] и ждёт. :::
:::p{html} Кошельки обычно показывают две кнопки на застрявшей [[gloss:transaction|TX]]: Speed up и Cancel. Под капотом оба делают одно и то же: отправляют новую транзакцию с тем же [[gloss:nonce|nonce]], но с более высокой ценой газа. Валидатор включит ту, что выгоднее, а вторая останется недействительной. Разница только в содержимом: :::
:::p{html}
- Speed up – та же [[gloss:transaction|TX]], что и оригинальная, но дороже. Если включится, вы получите ожидаемый результат, заплатив больше газа.
- Cancel – пустая [[gloss:transaction|TX]] (transfer 0 [[gloss:ethereum|ETH]] на свой адрес) с тем же [[gloss:nonce|nonce]] и более высоким газом. Если включится, оригинальная не пройдёт, но вы всё равно заплатите за пустой transfer (минимум 21 000 газа).
:::p{html} Важная деталь: cancel не гарантирован. Если оригинальная [[gloss:transaction|TX]] уже распространилась по сети и валидатор успел её взять раньше, чем cancel-[[gloss:transaction|TX]] добежала до builder, то включится именно оригинальная, и cancel пропадёт впустую. Особенно высока вероятность, если [[gloss:transaction|TX]] интересна [[gloss:mev|mev]]-боту (например, прибыльный swap): он добавит её в свой bundle и проведёт через приватный канал, ваша cancel-[[gloss:transaction|TX]] через публичный [[gloss:mempool|mempool]] никогда её не догонит. :::
:::p{html} RPC – это адрес узла, через который ваш кошелёк говорит с блокчейном (см. 1.2 §6 для введения). Подписали транзакцию → кошелёк отправляет её на RPC → RPC бродкастит её в сеть. Запрашиваете баланс → кошелёк запрашивает RPC. У каждого кошелька есть RPC по умолчанию: :::
:::p{html}
- MetaMask по умолчанию ходит через Infura. Infura принадлежит Consensys, той же компании, что и MetaMask. То есть Consensys видит все ваши запросы.
- Rabby использует публичные RPC для каждой сети, пользователь может настроить свой.
- Trust [[gloss:wallet|Wallet]] – свой набор узлов.
:::p{html} 20 октября 2025 года, когда AWS US-EAST-1 ушёл в downtime, Infura отключилась вместе с ним, и MetaMask на десятки минут стал недоступен. Параллельно «легли» все [[gloss:layer 2|L2]]: Arbitrum, Optimism, Base, Polygon, Linea, Scroll – потому что их публичные RPC тоже хостятся на AWS. [[gloss:ethereum|Ethereum]] продолжал производить блоки, но 99% пользователей не могли с ним общаться. Это конкретный случай централизации: «децентрализованная» инфраструктура на одной cloud-площадке. :::
:::p{html} Зачем менять RPC: :::
:::p{html}
- Приватность. Текущий RPC знает ваш IP, какие dApp вы используете, баланс кошелька, частоту запросов. Это коммерческие данные.
- Скорость. Если ваш RPC лагает (rate limit, перегрузка), [[gloss:transaction|TX]] отправляется секундами вместо мгновенно.
- [[gloss:mev|MEV]] protection. [[gloss:flashbots|Flashbots]] Protect и [[gloss:mev|MEV]] Blocker – это RPC, которые кладут [[gloss:transaction|TX]] в приватный пул, см. §3.
- Доступность. Если падает дефолтный RPC, иметь альтернативный в настройках – это страховка от ситуации 20 октября 2025.
:::p{html} Конкретный список RPC по задачам в Advanced 1. :::
:::p{html} Несколько типичных ситуаций, которые встречаются хотя бы раз у каждого: :::
:::p{html}
- Wrong chain / [[gloss:chainid|chainId]] mismatch. Вы на Arbitrum, dApp ожидает [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] (или наоборот). Подпись валидна только для конкретной сети ([[gloss:chainid|chainId]] зашит в подпись, см. §1), поэтому [[gloss:transaction|TX]] просто не пройдёт. UI обычно показывает «Switch network».
- Insufficient funds for [[gloss:gas|gas]]. На вашем адресе есть USDC, но нет [[gloss:ethereum|ETH]] (или нет [[gloss:ethereum|ETH]] в нативном балансе для оплаты газа). Решение: пополнить нативный токен сети.
- [[gloss:nonce|Nonce]] too low. Кошелёк попытался отправить [[gloss:transaction|TX]] с [[gloss:nonce|nonce]], который уже использован (например, после смены RPC кошелёк не обновил счётчик). Решение: переподключить кошелёк или вручную поправить [[gloss:nonce|nonce]].
- Slippage too high. Цена сместилась больше, чем вы разрешили. [[gloss:transaction|TX]] revert. См. §3 про slippage как защиту от [[gloss:mev|MEV]].
:::callout{icon=💡} Машина, которая «что-то не делает». Не заводится, не едет, мигает дашборд. Механик подключает диагностический сканер и читает коды: P0301 (cylinder 1 misfire), P0420 (catalytic converter efficiency), и так далее. Каждый код это конкретная неисправность, и каждой соответствует конкретное решение. Revert это код ошибки на дашборде: машина проехала немного и встала, бензин в баке убавился, но цели вы не достигли. Stuck [[gloss:transaction|TX]] это машина, которая стоит на холостом ходу: всё включено, мотор работает, но движения нет: возможно, не хватает мощности (низкий газ) или впереди затор. Speed-up это «дать больше газа», cancel это «развернуться и поехать домой» (вместо запланированной поездки получили пустой пробег). Сменить RPC это сменить автосервис: те же машины, те же запчасти, но другой персонал, другие очереди, другая стоимость диагностики. Каждый код ошибки в [[gloss:evm|EVM]], как и в OBD-сканере, имеет четкое значение, и обучение работать с [[gloss:defi|DeFi]] во многом это знакомство с этой таблицей. :::
:::keypoints{label="Ключевые моменты"}
- Многие воспринимают газ за неуспешную [[gloss:transaction|TX]] как штраф, но это та же стандартная плата (baseFee + priorityFee) × gasUsed. Разница только в значении gasUsed: для успешной это весь расход кода, для revert это расход до точки сбоя. baseFee сжигается в обоих случаях, priorityFee в обоих идёт валидатору. Сеть не отличает «успех» от «revert» с точки зрения экономики, только пользователь.
- Если вы отправили пять [[gloss:transaction|TX]] подряд ([[gloss:nonce|nonce]] 10, 11, 12, 13, 14), и [[gloss:transaction|TX]] #11 застряла из-за низкого газа, то #12-14 не пройдут, даже если их газ выше. Это специфика sequential [[gloss:nonce|nonce]]: блокчейн не пропустит #12, пока не включена #11. Кошелёк часто не показывает эту зависимость явно, просто три [[gloss:transaction|TX]] висят pending без объяснения. Решение всегда одно: speed-up или cancel самой ранней застрявшей [[gloss:transaction|TX]], остальные пройдут автоматически.
- В [[gloss:ethereum|Ethereum]]-протоколе нет настоящей команды «отмена». Кнопка Cancel в кошельке это UX-сокращение для «поставь ставку повыше на ту же позицию в очереди»: вы отправляете пустую [[gloss:transaction|TX]], которая повторно использует тот же [[gloss:nonce|nonce]] и обещает валидатору более крупную плату, рассчитывая, что он сочтёт её выгоднее для включения, чем ту, которую вы пытаетесь сбросить. Ментальная модель тут не «undo», а аукцион. Каждый раз, когда вы нажимаете Cancel, вы платите за шанс, а не покупаете возврат.
- Большинство относится к RPC как к технической мелочи, а это на самом деле то, кто видит вашу активность. Провайдер наблюдает всё, что через него проходит: адреса, балансы, тайминг запросов, выбор dApp. У MetaMask дефолт это Infura (владелец тот же, что и у кошелька), у Rabby публичные узлы. Сменить RPC значит выбрать другую сторону, которая будет знать о ваших действиях. AWS-сбой 20 октября 2025 добавил второй угол: один RPC это ещё и одна точка отказа. :::
:::details{badge=Практика title="Каталог RPC по задачам"} :::p{html}
- Standard commercial (Infura, Alchemy, QuickNode): high reliability, free tier for regular users, provider sees your requests. Addresses like https://[[gloss:mainnet|mainnet]].infura.io/v3/YOUR-PROJECT-ID or https://[[gloss:ethereum|eth]]-[[gloss:mainnet|mainnet]].g.alchemy.com/v2/YOUR-KEY.
- [[gloss:mev|MEV]] protection (see §3): https://rpc.[[gloss:flashbots|flashbots]].net, https://rpc.mevblocker.io. The [[gloss:transaction|TX]] goes into a private pool, bots can't see it. Trade-off: 1-2 [[gloss:block|blocks]] of inclusion latency.
- Public, privacy-focused: [[gloss:ethereum|eth]].llamarpc.com (free, no logging by their statement), 1rpc.io/[[gloss:ethereum|eth]] (zero-knowledge proxy, hides your IP), cloudflare-[[gloss:ethereum|eth]].com (Cloudflare).
- Your own [[gloss:node|node]]: run geth, erigon, or nethermind locally, point to http://localhost:8545. Full privacy and independence from external services, but requires ~2TB SSD and a machine that stays up.
- For [[gloss:layer 2|L2]]: each chain has its own RPC (Arbitrum arb1.arbitrum.io/rpc, Base [[gloss:mainnet|mainnet]].base.org, Optimism [[gloss:mainnet|mainnet]].optimism.io). Commercial providers also serve [[gloss:layer 2|L2]].
:::p{html} Текущий ваш RPC можно увидеть в кошельке: MetaMask Settings → Networks → [[gloss:ethereum|Ethereum]] [[gloss:mainnet|Mainnet]] → RPC URL. Чтобы добавить новый, идите в Networks → Add a network. Конкретный список адресов на май 2026: :::
:::p{html}
- Стандартный коммерческий (Infura, Alchemy, QuickNode): высокая надёжность, free tier для обычного пользователя, провайдер видит ваши запросы. Адреса вида https://[[gloss:mainnet|mainnet]].infura.io/v3/YOUR-PROJECT-ID или https://[[gloss:ethereum|eth]]-[[gloss:mainnet|mainnet]].g.alchemy.com/v2/YOUR-KEY.
- [[gloss:mev|MEV]]-защита (см. §3): https://rpc.[[gloss:flashbots|flashbots]].net, https://rpc.mevblocker.io. [[gloss:transaction|TX]] идёт в приватный пул, боты не видят. Trade-off – задержка включения 1-2 блока.
- Публичные с акцентом на приватность: [[gloss:ethereum|eth]].llamarpc.com (бесплатный, без логирования по их заявлениям), 1rpc.io/[[gloss:ethereum|eth]] (zero-knowledge proxy, скрывает IP), cloudflare-[[gloss:ethereum|eth]].com (Cloudflare).
- Свой узел: запустите geth, erigon или nethermind локально, используйте http://localhost:8545. Это даёт полную приватность и независимость от внешних сервисов, но требует ~2TB SSD и постоянной работы машины.
- Для [[gloss:layer 2|L2]]: каждая сеть имеет свой RPC (Arbitrum arb1.arbitrum.io/rpc, Base [[gloss:mainnet|mainnet]].base.org, Optimism [[gloss:mainnet|mainnet]].optimism.io). Также работают коммерческие провайдеры для [[gloss:layer 2|L2]].
:::p{html} Хорошая практика – добавить два RPC и переключаться вручную: один основной (например, [[gloss:flashbots|Flashbots]] для большинства swap'ов), один backup (например, llamarpc для read-only-операций). Если один лагает, второй спасает. ::: :::
:::details{badge=Глубже title="Эволюция типов транзакций и их revert'ов"} :::p{html}
- Legacy (type 0), before August 2021: one [[gloss:gas|gas]] price (gasPrice). Reverts were simple: out-of-[[gloss:gas|gas]], require failed, intrinsic [[gloss:gas|gas]] too low.
- [[gloss:eip|EIP]]-2930 (type 1), April 2021: added access lists for cheap inter-contract calls. Almost never used in practice. Has its own reverts for malformed lists.
- [[gloss:eip|EIP]]-1559 (type 2), August 2021: baseFee + priorityFee. A new error category appeared: "maxFeePerGas below baseFee" – the [[gloss:transaction|TX]] won't go through until the price drops.
- [[gloss:eip-4844|EIP-4844]] (type 3), March 2024 ([[gloss:dencun|Dencun]]): [[gloss:blob|blob]] [[gloss:transaction|transactions]] for [[gloss:layer 2|L2s]]. Reverts for [[gloss:blob|blob]] fee, [[gloss:blob|blob]] count too high, [[gloss:blob|blob]] [[gloss:hash|hash]] mismatch.
- [[gloss:eip-7702|EIP-7702]] (type 4), May 2025 ([[gloss:pectra|Pectra]]): [[gloss:eoa|EOA]] as contract. New errors: invalid authorization, code conflict, delegation rejected. Drainer attacks through 7702 were covered in §5.
:::p{html} Каждое крупное обновление протокола добавляло новый тип транзакции, и у каждого свои revert-сценарии. Это полезно знать, потому что error messages в кошельках напрямую отражают этот формат. :::
:::p{html}
- Legacy (тип 0), до августа 2021: одна цена газа (gasPrice). Revert'ы были простые: out-of-[[gloss:gas|gas]], require failed, intrinsic [[gloss:gas|gas]] too low.
- [[gloss:eip|EIP]]-2930 (тип 1), апрель 2021: добавлен access list для дешёвых вызовов между контрактами. Практически не используется. Свои revert'ы за неверный список.
- [[gloss:eip|EIP]]-1559 (тип 2), август 2021: baseFee + priorityFee. Появилась новая категория ошибок: «maxFeePerGas ниже baseFee» – [[gloss:transaction|TX]] не пройдёт, пока цена не упадёт.
- [[gloss:eip-4844|EIP-4844]] (тип 3), март 2024 ([[gloss:dencun|Dencun]]): [[gloss:blob|blob]]-транзакции для [[gloss:layer 2|L2]]. Revert'ы за [[gloss:blob|blob]] fee, [[gloss:blob|blob]] count too high, [[gloss:blob|blob]] [[gloss:hash|hash]] mismatch.
- [[gloss:eip-7702|EIP-7702]] (тип 4), май 2025 ([[gloss:pectra|Pectra]]): [[gloss:eoa|EOA]] как контракт. Новые ошибки: invalid authorization, code conflict, delegation rejected. Drainer-атаки через 7702 обсуждались в §5.
:::p{html} Когда видите в Etherscan error message с непонятным кодом, первое, что нужно сделать, это посмотреть на тип [[gloss:transaction|TX]] (поле Type) и понять, какие revert'ы для него характерны. На январь 2026 примерно 80% revert'ов на [[gloss:mainnet|mainnet]] это типы 2 (1559) и 3 ([[gloss:blob|blob]]), на [[gloss:layer 2|L2]] преобладает тип 3. ::: :::
:::quiz{id=q-3-3-s6 correct=1 explanation="Верно. Это классический [[gloss:nonce|nonce]]-gap. В [[gloss:ethereum|Ethereum]] [[gloss:nonce|nonce]] последователен: [[gloss:transaction|TX]] #6 не может быть включена, пока не включена #5, независимо от того, насколько выше газ у #6. Высокий газ на #6 и #7 в этой ситуации работает против вас: вы заплатили за приоритет, но приоритет недостижим, пока заблокирован [[gloss:nonce|nonce]]. Решение: speed-up именно [[gloss:nonce|nonce]] 5 (поднять её газ), после чего все три пройдут в порядке. A не работает: если бы перегружена была сеть, помог бы общий speed-up, но здесь проблема локальная. C не работает: при revert [[gloss:transaction|TX]] не висит pending, она включается в блок с failed-статусом. D – у [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] с 2015 года не было полной остановки."} Вы отправили в Uniswap три транзакции подряд: swap 100 USDC → [[gloss:ethereum|ETH]] ([[gloss:nonce|nonce]] 5, низкий газ), swap 200 DAI → [[gloss:ethereum|ETH]] ([[gloss:nonce|nonce]] 6, средний газ), add liquidity ([[gloss:nonce|nonce]] 7, высокий газ). Через 10 минут все три висят «pending» в Etherscan. Что произошло и что делать?
- Сеть [[gloss:ethereum|Ethereum]] перегружена, нужно подождать
- [[gloss:transaction|TX]] [[gloss:nonce|nonce]] 5 застряла из-за низкого газа, [[gloss:transaction|TX]] 6-7 ждут её. Нужен speed-up именно [[gloss:nonce|nonce]] 5
- Высокий slippage tolerance уронил все три в revert
- Сеть [[gloss:ethereum|Ethereum]] упала, надо проверить статус :::
:::section{mins=8} L2: те же правила :::
:::lead На Arbitrum или Base пользователь делает swap за 0,1 цента. На [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] тот же swap стоит 13 центов, в сто раз дороже. Правила те же, но [[gloss:layer 2|L2]] разделили работу иначе: исполнение происходит дёшево на отдельной цепи, а [[gloss:l1|L1]] используется только для финальной публикации сжатых данных. То же приложение, та же [[gloss:evm|EVM]], та же подпись пользователя, но цена в сто раз ниже, и под капотом несколько вещей работают иначе. :::
:::p{html} Транзакция на [[gloss:layer 2|L2]] платит дважды. Execution [[gloss:gas|gas]] – это плата за вычисления внутри сети [[gloss:layer 2|L2]]: рассчитывается по той же формуле (baseFee + priorityFee) × gasUsed, что и на [[gloss:l1|L1]] (см. §2), но baseFee тут в стотысячных Gwei. [[gloss:l1|L1]] data cost – это плата за публикацию сжатых данных вашей транзакции на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]], потому что в конечном счёте именно [[gloss:l1|L1]] хранит историю [[gloss:layer 2|L2]] и гарантирует её безопасность. Эта вторая часть и есть основной вклад в цену swap на [[gloss:layer 2|L2]]: в типичной транзакции 80-90% стоимости приходится именно на [[gloss:l1|L1]] publication, а не на сами вычисления. До марта 2024 года [[gloss:l1|L1]] data cost доминировал, и swap на Arbitrum стоил 10-20 центов. После активации [[gloss:eip-4844|EIP-4844]] ([[gloss:dencun|Dencun]]) появились [[gloss:blob|blob]]-транзакции, в которых данные хранятся ограниченное время (около 18 дней) и стоят на порядки дешевле обычной calldata. После [[gloss:dencun|Dencun]] типичный swap на Base стоит 0,1 цента вместо 10-20 центов. :::
:::p{html} На [[gloss:ethereum|Ethereum]] [[gloss:l1|L1]] [[gloss:mempool|mempool]] публичный (см. §3): любой видит pending [[gloss:transaction|TX]], любой бот может прочитать вашу транзакцию до включения в блок. На [[gloss:layer 2|L2]] архитектура другая. У каждого [[gloss:layer 2|L2]] есть центральный [[gloss:sequencer|sequencer]] – это единая точка, которая принимает входящие транзакции, упорядочивает их и публикует batch на [[gloss:l1|L1]]. На Arbitrum [[gloss:sequencer|sequencer]] управляется Offchain Labs, на Optimism – OP Labs, на Base – Coinbase. Это значит несколько вещей: публичного [[gloss:mempool|mempool]] в обычном понимании на [[gloss:layer 2|L2]] нет, sandwich-атак через bot-front-running значительно меньше, но появляется одна точка отказа. 20 октября 2025 AWS US-EAST-1 ушёл в downtime, и Arbitrum, Optimism, Base, Polygon, Linea, Scroll одновременно стали недоступны: их [[gloss:sequencer|sequencer]]'ы хостятся на AWS. Force inclusion (запасной механизм отправки [[gloss:transaction|TX]] напрямую через [[gloss:l1|L1]], обходя [[gloss:sequencer|sequencer]]) технически работает, но занимает ~24 часа и подходит только для критических случаев цензуры, а не для обычного swap. :::
:::p{html} Поскольку на [[gloss:layer 2|L2]] нет публичного [[gloss:mempool|mempool]], классическая sandwich-атака через [[gloss:mempool|mempool]]-боты (см. §3) технически невозможна. Это объясняет почему на Arbitrum или Base пользователи в 2026 году практически не сталкиваются с sandwich-потерями. Но [[gloss:mev|MEV]] не исчезает: его источник смещается. Теперь [[gloss:sequencer|sequencer]] сам может в теории извлекать [[gloss:mev|MEV]], упорядочивая транзакции в своём блоке – [[gloss:l2beat|L2BEAT]] прямо фиксирует это как известный риск для любого централизованного [[gloss:sequencer|sequencer]]. На Base [[gloss:sequencer|sequencer]] упорядочивает транзакции по убыванию priorityFee, поэтому searcher'ы конкурируют через размер чаевых, а часть извлечённой стоимости достаётся Coinbase как [[gloss:sequencer|sequencer]] revenue (это прописано в их 10-K [[gloss:sec-regulator|SEC]]). Arbitrum использует timestamp-based ordering, который сложнее эксплуатировать, но [[gloss:mev|MEV]] полностью не исключает. На практике массового [[gloss:mev|MEV]]-извлечения со стороны оператора ни на одной из сетей не задокументировано в 2024-2026; сдерживающий фактор тут репутационный, а не криптографический. Для пользователя, делающего крупный swap на [[gloss:layer 2|L2]], угрозы того же масштаба, что на [[gloss:l1|L1]] (см. кейс марта 2025 в §3), нет, но допущение о доверии другое: вы доверяете заинтересованности оператора не попадаться, а не криптографической гарантии. :::
:::p{html} На [[gloss:l1|L1]] транзакция попадает в блок за 12 секунд и считается финализированной примерно через 13 минут (две эпохи). На [[gloss:layer 2|L2]] [[gloss:sequencer|sequencer]] формирует «soft [[gloss:block|block]]» за 250 миллисекунд – 2 секунды: для пользователя транзакция практически мгновенная. Но это soft confirmation от [[gloss:sequencer|sequencer]]'а, не финальная безопасность. Полная финализация происходит, когда [[gloss:layer 2|L2]] публикует batch на [[gloss:l1|L1]] и проходит challenge window: на optimistic [[gloss:rollup|rollups]] (Arbitrum, Optimism, Base) это 7 дней, на ZK [[gloss:rollup|rollups]] (zkSync, Linea, Scroll, Starknet) – от часа до нескольких часов с момента публикации [[gloss:validity proof|validity proof]]. Для обычной деятельности внутри [[gloss:layer 2|L2]] разница не критична: вы свапаете, минтите, кладёте в pool – всё это происходит мгновенно с точки зрения соответствующего [[gloss:layer 2|L2]]. Challenge window становится важным только когда вы выводите средства с [[gloss:layer 2|L2]] обратно на [[gloss:l1|L1]] (withdrawal). Для быстрого вывода существуют bridge'ы вроде Across, Hop, LayerZero, которые дают вам средства на [[gloss:l1|L1]] сразу, забирая комиссию за то, что сами ждут окончание challenge window. :::
:::p{html} Большая часть [[gloss:defi|DeFi]]-практик переносится с [[gloss:l1|L1]] на [[gloss:layer 2|L2]] без изменений, но несколько вещей нужно держать в голове: :::
:::p{html}
- Каждая [[gloss:layer 2|L2]] это отдельная сеть со своим [[gloss:chainid|chainId]]. Подпись для Arbitrum ([[gloss:chainid|chainId]] 42161) не работает на Optimism ([[gloss:chainid|chainId]] 10) или Base ([[gloss:chainid|chainId]] 8453). Если кошелёк не на нужной сети, [[gloss:transaction|TX]] просто не пройдёт.
- [[gloss:nonce|Nonce]] независим на каждой сети. Адрес у вас тот же (для [[gloss:eoa|EOA]]), но счётчик [[gloss:nonce|nonce]] ведётся отдельно на каждом [[gloss:layer 2|L2]]. Это не пересекается с [[gloss:l1|L1]].
- Allowance и Permit2 разрешения не переносятся. Allowance это запись в storage контракта токена. USDC на [[gloss:mainnet|mainnet]] и USDC на Arbitrum это два разных контракта со своим storage. Approve, который вы делали год назад на [[gloss:mainnet|mainnet]], на Arbitrum не виден. Permit2 имеет тот же адрес 0x000…22D473 на всех [[gloss:evm|EVM]]-chains (он deployed через CREATE2), но allowance ему тоже нужен отдельно на каждой сети.
- Revoke.cash работает отдельно на каждой сети. Если делаете cleanup, переключайтесь между сетями в верхнем меню сайта (см. §5).
- RPC для [[gloss:layer 2|L2]] отличается от [[gloss:l1|L1]]. У Arbitrum это arb1.arbitrum.io/rpc, у Base [[gloss:mainnet|mainnet]].base.org, у Optimism [[gloss:mainnet|mainnet]].optimism.io (см. §6 Advanced 1).
:::callout{icon=💡} Главный международный аэропорт и десяток региональных. Самолёты летают по тем же правилам ICAO, экипажи проходят те же сертификации, грузы попадают по тем же таможенным стандартам. Но цена билета в крупном хабе с длинными рейсами высокая, а на региональном маршруте между двумя малыми городами – копеечная. Региональные аэропорты дешёвые потому что они не делают всё сами: их рейсы стыкуются с крупным хабом для дальнейшей пересылки. Если что-то идёт не так в крупном хабе, страдают и региональные: их собственные рейсы летают, но пассажиры не могут попасть в международную сеть. Так же [[gloss:layer 2|L2]] свои транзакции исполняют у себя дёшево и быстро, но финальная «легализация» этих транзакций происходит через [[gloss:l1|L1]]. AWS-сбой 20 октября 2025 показал ещё одну сторону аналогии: если у нескольких региональных аэропортов один и тот же диспетчерский центр, отказ этого центра останавливает их одновременно, даже если сами полосы в порядке. :::
:::keypoints{label="Ключевые моменты"}
- Когда вы видите цену 0,001 доллара за swap на Base, около 80-90% этой суммы уходит на [[gloss:l1|L1]]: за публикацию [[gloss:blob|blob]]-транзакции с данными вашего swap. Сами вычисления внутри Base стоят копейки. Это объясняет странность: простой transfer на [[gloss:layer 2|L2]] стоит почти столько же, сколько сложный swap, потому что публикуемые байты у них сравнимы по объёму. И поэтому когда [[gloss:l1|L1]] baseFee взлетает (редко, но бывает), цены на всех [[gloss:layer 2|L2]] синхронно растут.
- На [[gloss:l1|L1]] валидаторы это открытый набор тысяч независимых участников. На [[gloss:layer 2|L2]] [[gloss:sequencer|sequencer]] это конкретное юрлицо: Offchain Labs у Arbitrum, OP Labs у Optimism, Coinbase у Base. Это даёт скорость и предсказуемость, но забирает криптографическую гарантию: при цензуре со стороны [[gloss:sequencer|sequencer]]'а у вас остаётся только force inclusion через [[gloss:l1|L1]] с задержкой в сутки. В 2026 крупные операторы ведут себя честно, но эта дисциплина репутационная, не криптографическая – другая категория защиты, чем трастлесс [[gloss:l1|L1]].
- Адрес у вас один и тот же на всех [[gloss:evm|EVM]]-сетях (для [[gloss:eoa|EOA]]), но storage в контрактах токенов разный. Если вы делали approve(Permit2) на USDC [[gloss:mainnet|mainnet]], на USDC Arbitrum этого разрешения нет, нужно делать заново. То же касается [[gloss:nonce|nonce]], balance, allowance ко всем dApp. Контр-интуитивно для тех, кто думает «у меня тот же кошелёк». На уровне cleanup'а через revoke.cash это значит, что нужно делать его на каждой сети отдельно.
- Внутри [[gloss:layer 2|L2]] ваша транзакция включается за секунды и сразу выполняется: swap, deposit, mint происходят моментально. Challenge window в 7 дней (для optimistic [[gloss:rollup|rollups]]) или в час (для ZK-[[gloss:rollup|rollups]]) – это про то, когда вывод с [[gloss:layer 2|L2]] на [[gloss:l1|L1]] становится криптографически безопасным. Для повседневной работы внутри [[gloss:layer 2|L2]] это не важно. Для крупных withdrawal'ов это страховка: либо ждать challenge window, либо использовать платный fast bridge, который берёт на себя риск. :::
:::details{badge=Глубже title="Force inclusion и censorship resistance"} :::p{html} В теле секции сказано: force inclusion существует как escape hatch; архитектурное обоснование лежит в 2.5 §3. Здесь – конкретная техника: какой контракт на [[gloss:l1|L1]] вызывать и какую задержку ожидать на каждом роллапе. :::
:::p{html} Arbitrum One: контракт Inbox на [[gloss:l1|L1]] предоставляет sendL2Message(bytes calldata messageData). Закодированная [[gloss:layer 2|L2]]-транзакция попадает в delayed inbox, и [[gloss:sequencer|sequencer]] обязан её упорядочить в течение примерно 24 часов. Если [[gloss:sequencer|sequencer]] не делает этого, любой может вызвать forceInclusion() на bridge-контракте, чтобы вставить транзакцию напрямую. :::
:::p{html} Optimism, Base и другие [[gloss:op stack|OP Stack]]-цепочки: депозиты идут через метод depositTransaction у контракта OptimismPortal (нижележащий messenger – L1CrossDomainMessenger). В нормальном режиме депозиты появляются на [[gloss:layer 2|L2]] примерно в течение 30 минут. Если [[gloss:sequencer|sequencer]] отключён, протокол гарантирует включение не позднее конца 12-часового sequencing-окна – заметно быстрее arbitrum-задержки в 24 часа, ценой [[gloss:l1|L1]]-газа в момент отправки. :::
:::p{html} ZK-роллапы (Scroll, Linea, zkSync, Starknet): у каждого свой [[gloss:l1|L1]] entry-контракт со схожей семантикой. Задержки варьируются от минут до часов в зависимости от ритма публикации proof'ов конкретной цепочки. :::
:::p{html} Во всех случаях стоимость [[gloss:l1|L1]]-газа делает force inclusion непрактичным для swap на 50 долларов, но реалистичным для withdrawal значимого размера, когда [[gloss:sequencer|sequencer]] не отвечает – ровно тот failure mode, который ненадолго продемонстрировал AWS-сбой 20 октября 2025. ::: :::
:::quiz{id=q-3-3-s7 correct=2 explanation="Верно. Адрес вашего кошелька один и тот же на всех [[gloss:evm|EVM]]-сетях, но это не значит, что storage контрактов одинаковый. USDC на [[gloss:mainnet|mainnet]] и USDC на Arbitrum – это два разных смарт-контракта на двух разных сетях, у каждого своё storage. Approve, который вы сделали год назад на [[gloss:mainnet|mainnet]], записал «разрешение Permit2 тратить ваши USDC» в storage [[gloss:mainnet|mainnet]]-контракта USDC. На Arbitrum-контракте USDC такой записи нет. Поэтому нужен новый approve. B неточно: Permit2 имеет один и тот же адрес 0x000000000022D473030F116dDEE9F6B43aC78BA3 на всех [[gloss:evm|EVM]]-chains, потому что был deployed через CREATE2 с одним и тем же init code, поэтому это тот же контракт-посредник. A не работает: MetaMask не «забывает» approve, потому что approve хранится не в кошельке, а в контракте. D неправильно: тот же ERC-20 стандарт."} Вы делали swap USDC → [[gloss:ethereum|ETH]] на Uniswap V3 на [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] в феврале 2024, и MetaMask установил approve(Permit2, unlimited) для вашего USDC. Сегодня вы решаете попробовать тот же Uniswap V3 на Arbitrum One, тем же кошельком, тем же адресом. При первом swap MetaMask снова требует подписать approve. Почему?
- MetaMask забыл предыдущий approve, нужно переподключить кошелёк
- Permit2 на Arbitrum это другой контракт, отличный от [[gloss:mainnet|mainnet]]
- Каждая [[gloss:layer 2|L2]] это отдельная сеть со своим storage; allowance на [[gloss:mainnet|mainnet]] USDC и Arbitrum USDC хранятся раздельно
- Uniswap V3 on [[gloss:layer 2|L2]] использует другой токен-стандарт, не ERC-20 :::
:::section{mins=7} Перед подписью :::
:::lead Каждая подпись в Web3 происходит в зазоре между «нажать Confirm» и «не нажать Confirm» – это несколько секунд, в которые умещается всё, что вы прошли в этой карточке. Эта последняя секция – алгоритм для этих секунд: что увидеть на экране кошелька, что проверить во вкладке браузера, какие красные флаги отменяют операцию автоматически. Не теория, а ритуал. :::
:::p{html} Когда кошелёк всплывает с запросом, у вас есть несколько секунд осмотреть его. Минимум, который должен быть виден до того, как ваш палец двигается к кнопке Confirm: :::
:::p{html}
- Тип запроса в верхней строке: eth_sendTransaction, personal_sign, eth_signTypedData, или (красный флаг) eth_sign – см. §4.
- Для транзакции: получатель (to), сумма (value), декодированный data, если кошелёк его показывает. Если data – это голый hex, это уже сигнал замедлиться.
- Для [[gloss:eip-712|EIP-712]]: имя dApp в domain.name, адрес контракта в domain.verifyingContract, идентификатор сети в domain.[[gloss:chainid|chainId]], расшифровка message.
- Результат симуляции, если кошелёк его показывает (Rabby, MetaMask с Blockaid). «You will receive X, you will send Y» – если симуляция показывает что-то странное или ничего, это сигнал отменить.
- Origin: какой сайт прислал запрос. Должен совпадать с тем, что в адресной строке вашего браузера. Расхождение это типичная clipboard-malware атака.
:::p{html} Экран кошелька показывает только то, что пришло из dApp. Перед тем как подписать, нужно убедиться, что сам dApp – тот, за кого себя выдаёт. :::
:::p{html}
- URL в адресной строке – прочитайте его дважды, посимвольно. uniswap.org и unisw4p.org выглядят одинаково на скриншоте, но это разные домены. Подмены через символы Unicode или через близкие буквы (l → 1, o → 0) – массовая практика drainer-сайтов.
- HTTPS и сертификат: зелёный замок должен быть. Если браузер ругается на сертификат, отмените и проверьте.
- Как вы попали на этот сайт: если вы зашли сами по сохранённой закладке – это безопасный путь. Если перешли по ссылке из DM, email, твита, Telegram – это уровень риска выше, всё проверяйте втрое.
- Origin в запросе кошелька должен совпадать с URL вкладки. Когда не совпадает, это либо clipboard-malware, либо встроенный фрейм с фишинг-сайта поверх legit-сайта. И то, и другое – отмена.
:::p{html} Это список ситуаций, при которых вы не думаете дальше, а нажимаете Cancel и потом разбираетесь. Чеклист устроен так, чтобы в момент принятия решения не было сомнений: :::
:::p{html}
- eth_sign-запрос на подпись произвольного [[gloss:hash|hash]]. См. §4: метод deprecated, валидный dApp его не использует.
- Unlimited allowance (число с 78 цифрами, заканчивающееся на ...9935) для разовой операции. [[gloss:permit|Permit]]/approve на конкретную сумму – норма; unlimited – норма только для активного [[gloss:defi|DeFi]]-pipeline, не для airdrop claim.
- Deadline в далёком будущем: December 31, 2099 или аналогичное. Любой honest dApp ставит deadline на минуты или часы.
- Spender, которого вы не узнаёте. Откройте его в Etherscan: если контракт создан вчера, имеет 3 транзакции, это не Uniswap.
- [[gloss:hash|Hash]] вместо декодированной структуры: кошелёк показывает 0x4a8c2d... и больше ничего. Это blind signing, см. §4.
- URL в браузере не тот, который вы ожидали: даже если разница в одной букве.
- Кошелёк показывает предупреждение через Blockaid или встроенную защиту Rabby. Эти сервисы знают тысячи известных drainer-доменов, доверяйте им.
- Сумма больше, чем вы планировали. Даже если на доли процента, это сигнал проверить ещё раз.
- Запрос [[gloss:eip-7702|EIP-7702]] от сайта, который вы не открывали с этой целью. [[gloss:eip-7702|EIP-7702]] нужно для конкретных операций (smart [[gloss:wallet|wallet]], batch); если сайт делает обычный swap и просит 7702 – отмена.
:::p{html} Чеклист, который вы прочитали один раз и забыли, не защищает. Защищает привычка – автоматическая пауза 3-5 секунд между запросом кошелька и нажатием Confirm. За эти секунды глаза успевают пройтись по экрану и заметить расхождение, прежде чем мозг рационализирует «вроде всё нормально». Drainer-атаки в 2025-2026 годах построены ровно на отсутствие этой паузы: красивый интерфейс airdrop-сайта, кошелёк показывает [[gloss:permit|permit]], пользователь жмёт Confirm, потому что «я же на claim-странице, всё в порядке». Hardware-[[gloss:wallet|wallet]] с большим экраном (Ledger Stax/Flex, Trezor Safe 5) физически замедляет процесс: подтверждение требует нажатия отдельной кнопки, после прочтения декодированного содержания. Это и есть встроенная пауза. Большинство людей, теряющих средства через drainer-атаки, технически не наивны – они торопятся. Если торопитесь, не подписывайте: дешевле потерять час, чем подпись. :::
:::callout{icon=💡} Pre-flight checklist пилота гражданской авиации. Перед каждым полётом, независимо от того, сколько у пилота лет за штурвалом, он открывает бумажный или электронный список и проходит по пунктам: топливо, закрылки, температура, давление, радиосвязь, светофор от диспетчера. Чтение занимает 2-3 минуты. Опытный пилот может пройти его на автопилоте, но он его делает, потому что есть документированные случаи, когда отсутствие checklist'а стоило жизней. То же самое с подписью в Web3: каждый раз перед Confirm – один и тот же короткий проход по экрану. Опытный [[gloss:defi|DeFi]]-пользователь может сделать это за 5 секунд, но он его делает. Когда после ста подписей пользователь начинает «и так нажимать» – это эквивалент пилоту, который пропустил checklist «потому что и так знаю самолёт». В этой точке катастрофа становится вопросом времени. :::
:::keypoints{label="Ключевые моменты"}
- После нажатия Confirm вас в процессе больше нет. Контракт выполняет действие, опираясь на вашу подпись как на доверенность. Эту особенность нельзя «отменить позже» – подпись валидна, как мы видели в §4, до явного отзыва. Поэтому весь смысл проверки приходится на момент до Confirm. Психологически это контр-интуитивно: пользователь воспринимает Confirm как «совершить покупку», а это «выписать разрешение тратить». Разница в том, что покупка одноразова, а разрешение продолжает работать.
- На экране Ledger Stax или Trezor Safe 5 [[gloss:eip-712|EIP-712]] структура декодируется устройством самостоятельно, не доверяя dApp в браузере. Software-кошелёк (MetaMask, Rabby) живёт в том же браузере, что и dApp, и видит ту же информацию (см. §4). Если dApp подменён, software-кошелёк покажет «правильную» подпись, а под ней будет другая. Hardware с большим экраном – единственный способ увидеть то, что реально подписывается. Сумма не важна: $50 или $50,000, механизм атаки одинаковый.
- Crypto Twitter полон историй про инженеров, аналитиков, разработчиков смарт-контрактов, теряющих десятки тысяч через клик «claim airdrop». У этих людей нет проблемы с пониманием, как работают подписи; у них есть проблема с паузой перед Confirm. Защита тут не знания, а темп. Тот, кто всегда тратит 5 секунд перед подписью, защищён лучше, чем тот, кто прочитал документацию Uniswap, но подписывает быстро.
- Самая частая ошибка – нажимать Confirm «потому что уже здесь оказался»: sunk-cost мышление. На самом деле отмена не стоит ничего ([[gloss:transaction|TX]] не отправлена, газа не списано), а возможная потеря может быть огромной. Когда возникает сомнение, отмена – всегда правильное действие. Можно вернуться через час, после проверки, и подписать снова. Можно вообще не возвращаться. Никакой dApp не пострадает от того, что вы передумали в последний момент. :::
:::details{badge=Практика title="Минимальный security setup на 2026"} :::p{html}
- A hardware [[gloss:wallet|wallet]] with a large screen: Ledger Stax, Ledger Flex, or Trezor Safe 5. They decode [[gloss:eip-712|EIP-712]] structures independently and physically slow the signing process down. Price $250-400, which on average pays for itself on the first potentially prevented attack.
- An extension [[gloss:wallet|wallet]] with pre-sign simulation: Rabby by default, or MetaMask with Blockaid turned on. They show the [[gloss:transaction|transaction]] result before signing and [[gloss:block|block]] known phishing domains.
- Browser bookmarks for frequently used dApps: app.uniswap.org, app.aave.com, and so on. Never type URLs by hand, never click links from social media.
- Regular cleanup via revoke.cash: every 3 months, revoke allowances for dApps you don't use anymore. On each network separately (see §7). Cost: $1-3 for the whole cleanup on [[gloss:mainnet|mainnet]], pennies on [[gloss:layer 2|L2]].
- A backup receiving [[gloss:wallet|wallet]]: a new address with nothing on it and that has signed nothing. If your main [[gloss:wallet|wallet]] is compromised, everything moves here first. Keep this address in hardware, never connect it to a dApp.
:::p{html} Конкретный набор, который снижает почти весь риск drainer-атак до нуля: :::
:::p{html}
- Hardware [[gloss:wallet|wallet]] с большим экраном: Ledger Stax, Ledger Flex или Trezor Safe 5. Они декодируют [[gloss:eip-712|EIP-712]] структуры независимо и физически замедляют процесс подписи. Цена $250-400, в среднем окупается на первой потенциально предотвращённой атаке.
- Кошелёк-extension с pre-sign simulation: Rabby по умолчанию или MetaMask с включённым Blockaid. Они показывают результат транзакции до подписи и блокируют известные phishing-домены.
- Закладки в браузере для часто используемых dApp: app.uniswap.org, app.aave.com, и так далее. Никогда не вводите URL вручную, никогда не переходите по ссылкам из соцсетей.
- Регулярный cleanup через revoke.cash: каждые 3 месяца отзывайте allowance'ы у dApp, которыми не пользуетесь. На каждой сети отдельно (см. §7). Стоимость: 1-3 доллара за весь cleanup на [[gloss:mainnet|mainnet]], копейки на [[gloss:layer 2|L2]].
- Запасной кошелёк-приёмник: новый адрес, на котором ничего нет и который ничего не подписывал. Если ваш основной кошелёк подменён, всё переводится сюда первым делом. Этот адрес храните в hardware, никогда не подключайте к dApp.
:::p{html} Затраты на полный setup: 250-400 долларов на железо + час времени на установку. Это минимальный порог входа в безопасный [[gloss:defi|DeFi]]. ::: :::
:::details{badge=Глубже title="Если уже подписали что-то плохое: первые 30 минут"} :::p{html}
- If you signed a [[gloss:permit|permit]] or permit2 on a phishing site: immediately go to revoke.cash from a different device (or at least a different browser), and revoke all allowances on Permit2 and on the tokens that may be at risk. Speed matters – the drainer can drain in seconds once they notice you're trying to revoke.
- If a drain has already partially happened: transfer the remaining funds to a new address immediately. Don't try to revoke on the compromised one – the attacker can drain in parallel with your revoke [[gloss:transaction|transaction]].
- If you signed an [[gloss:eip-7702|EIP-7702]] delegation: the revocation procedure is specific – sign a new 7702 with an empty contract (the zero address) as the delegate. That returns your [[gloss:eoa|EOA]] to its normal state. If unsure how, just [[gloss:move|move]] funds to a new address and abandon the old one.
- If you suspect your [[gloss:seed phrase|seed phrase]] is leaked: new [[gloss:wallet|wallet]] right now, on a new machine, hardware. The old [[gloss:wallet|wallet]] can't be "cleaned" – its [[gloss:seed phrase|seed phrase]] is already with the attacker. [[gloss:move|Move]] everything, leave it empty.
:::p{html} Drainer-атаки обычно не выводят средства сразу. Между подписью и drain'ом проходит от нескольких минут до нескольких часов, иногда дней – атакующий ждёт, пока вы не подпишете ещё что-то или пока цены не сдвинутся. У вас есть окно для реакции. :::
:::p{html}
- Если подписали [[gloss:permit|permit]] или permit2 на phishing-сайте: немедленно зайдите на revoke.cash с другого устройства (или хотя бы из другого браузера), отзовите все allowance'ы у Permit2 и у токенов, которые могли попасть под удар. Скорость важна – drainer может drain'нуть за секунды, как только заметит, что вы пытаетесь отозвать.
- Если drain уже частично произошёл: остальные средства переведите на новый адрес немедленно. Не пытайтесь revoke на старом – атакующий может drain'нуть параллельно с вашей revoke-транзакцией.
- Если подписали [[gloss:eip-7702|EIP-7702]] delegate: процедура отзыва специфическая – нужно подписать новую 7702 с пустым контрактом (zero address) как делегатом. Это вернёт ваш [[gloss:eoa|EOA]] в обычное состояние. Если не уверены, как – переведите средства на новый адрес, а старый бросьте.
- Если подозреваете утечку [[gloss:seed phrase|seed phrase]]: новый кошелёк прямо сейчас, на новой машине, hardware. Старый кошелёк нельзя «вылечить», его сид-фраза уже у атакующего. Перенесите всё, оставьте пустой.
:::p{html} Стоимость exit'а: 10-30 долларов на газ за все revoke и transfer на [[gloss:mainnet|mainnet]], копейки на [[gloss:layer 2|L2]]. По сравнению с возможной потерей – пустяки. Главное – действовать в первые минуты, не часы. ::: :::
:::quiz{id=q-3-3-s8 correct=2 explanation="Верно. Даже когда URL правильный и контракт настоящий, unlimited + deadline 2099 – это сами по себе красные флаги. Это эквивалент бессрочного разрешения тратить любое количество USDC, и оно остаётся валидным буквально до конца века. Никакой honest dApp не нуждается в таком разрешении для одного swap'а – Uniswap V3 в нормальном flow запрашивает [[gloss:permit|permit]] на конкретную сумму с deadline в часах. Если интерфейс просит unlimited + far deadline, это либо подмена через подменённый JS, либо настройка по умолчанию, которую можно (и нужно) переопределить – большинство кошельков позволяют редактировать value до подписи. A и B неверны: правильность одного-двух полей не оправдывает игнорирование двух красных флагов в других полях. D неверно: V3 и V4 сосуществуют, V3 не «устарел»."} Вы открываете MetaMask на ноутбуке и видите запрос на подпись. Кошелёк показывает: тип eth_signTypedData, domain.name: «Uniswap V3», verifyingContract: 0xE592...64 (известный адрес роутера Uniswap), [[gloss:chainid|chainId]]: 1, message: [[gloss:permit|permit]] USDC, value: unlimited, spender: 0xE592...64, deadline: December 31, 2099. URL во вкладке браузера: app.uniswap.org. Что вы делаете?
- Подписать – это Uniswap, известный контракт
- Подписать – domain.name и URL совпадают, контракт правильный, всё в порядке
- Отменить – unlimited + deadline 2099 это сами по себе красные флаги, даже при правильном URL и контракте
- Отменить – Uniswap V3 устарел, нужно перейти на V4 :::