Transactions in Practice

Concepts: Anatomy of a Transaction, Transaction Lifecycle, Transaction Types and Costs, Gas in Practice, Approvals and Permits, What Can Go Wrong, L2 Transactions, Safe Transaction Checklist

:::section{mins=5} The Signed Instruction Inside :::

:::lead Pressing "Send" in a [[gloss:wallet|wallet]] does not send money. It assembles a structure of nine fields, [[gloss:hash|hashes]] the whole thing, signs the [[gloss:hash|hash]] with your [[gloss:private key|private key]], and ships the resulting package to the network. From that moment the decision is not yours: the network either accepts the package or it doesn't. A signature cannot be revoked once it leaves the [[gloss:wallet|wallet]]. :::

:::p{html} Take an ordinary path: you opened Uniswap, picked a swap of 100 USDC for [[gloss:ethereum|ETH]], pressed Confirm in Rabby. The [[gloss:wallet|wallet]] assembled nine fields on your behalf – what, to whom, with which parameters, on which network, on what terms for [[gloss:gas|gas]]. The signature locks in exactly those nine, in the form they have at the moment of signing. If the [[gloss:wallet|wallet]]'s decoder showed you a coherent picture, the bytes match the intent. If the decoder showed "unknown function" or raw bytes, the signature locks in regardless – you just won't have seen what. Every field discussed below follows from this one fact: a signature commits to specific bytes, not to the action you thought you were approving. :::

:::p{html} An [[gloss:ethereum|Ethereum]] [[gloss:transaction|transaction]] is made of nine fields. They split by meaning into three groups: what goes to whom, what you'll pay for it, and where and in what order it lands. The signature commits to all nine at once – which is why each of them matters. :::

:::p{html}

GroupFieldWhat it setsExample
What goes to whomfromSender (your address)0xd8dA…6045
toRecipient: [[gloss:wallet|wallet]] or contract0x7a25…488D
value[[gloss:ethereum|ETH]] to part with0.05 [[gloss:ethereum|ETH]]
dataEncoded contract function call0x38ed1739…
What you'll pay[[gloss:gas|gas]] limitCap on [[gloss:gas|gas]] for this [[gloss:transaction|TX]]180,000
maxFeePerGasCap on price per [[gloss:gas|gas]] unit5 Gwei
maxPriorityFeeTip to the [[gloss:validator|validator]]0.5 Gwei
Queue and network[[gloss:nonce|nonce]]Sequence number from your address42
[[gloss:chainid|chainId]]Network identifier1 ([[gloss:ethereum|Ethereum]])
:::

:::p{html} Of the nine fields in the table above, data is the one that hides what's actually happening. On a plain [[gloss:ethereum|ETH]] transfer it is empty, and there is nothing to inspect. On any interaction with a contract – a swap, an approve, a deposit, a mint – the field carries an instruction compressed into hex: "call this function with these arguments". Without a decoder, those bytes look like noise: 0x38ed1739000000000000000000000000a0b86991…. [[gloss:wallet|Wallets]] like Rabby or the current MetaMask read the field and render it in plain language: "you are calling swapExactTokensForTokens on Uniswap V2 Router, swapping 100 USDC for [[gloss:ethereum|ETH]] with a minimum out of 0.045 [[gloss:ethereum|ETH]]". When that decoding step is missing, you are signing bytes you cannot read. Almost every expensive loss of the past two years starts on this line; the largest single example, the February 2025 Bybit hack, gets a closer look in §4. :::

:::p{html} A signature is not a general nod of approval. The [[gloss:wallet|wallet]] takes all nine fields, collapses them into a single keccak256 [[gloss:hash|hash]], and signs that [[gloss:hash|hash]] with your [[gloss:private key|private key]] using [[gloss:ecdsa|ECDSA]]. Any change to any field produces a different [[gloss:hash|hash]], and the old signature is no longer valid against the new one. Two consequences follow that tend to surprise people. The first is atomicity: a [[gloss:transaction|transaction]] executes in full or not at all. If the contract hits a failure at any step, every state change rolls back, but the [[gloss:gas|gas]] spent on the attempt is gone. This is what "revert" means: you paid for the computation and walked away with nothing. The second is irreversibility: a signed [[gloss:transaction|transaction]] is not a bank transfer you can cancel. You can only outrun it with another [[gloss:transaction|transaction]] using the same [[gloss:nonce|nonce]] and a higher [[gloss:gas|gas]] price (the speed-up or cancel pattern covered in §6). :::

:::callout{icon=💡} An international shipping waybill at a customs broker's warehouse. The form has nine boxes: who is sending, to whom, what's inside, declared value, which logistics tariff, destination country, the sender's serial number for this shipment. The sender signs at the bottom, and that signature commits to exactly the boxes as they are written that moment. If anyone adds a zero to the declared value or changes the destination after the fact, the broker's stamp no longer matches the form, and the container goes back to the warehouse. And there is no such thing as "shipping partially". The container either left for the port on schedule, or stayed in the queue, or got turned back – but the broker's handling fee is already paid. :::

:::keypoints{label="Key Points"}

  • The value field only measures the network's native token – [[gloss:ethereum|ETH]] on [[gloss:ethereum|Ethereum]], MATIC on Polygon, nothing else. A $10,000 USDC transfer carries value = 0, and the amount itself moves through data, as a transfer() call on the USDC contract. A "zero-[[gloss:ethereum|ETH]]" [[gloss:transaction|transaction]] on Etherscan can be a multi-million-dollar one; what tells you the truth is data, not value.
  • An [[gloss:ecdsa|ECDSA]] signature is a mathematical proof that the [[gloss:transaction|transaction]] originates from the owner of the address in from. It says nothing about whether to holds the right address. [[gloss:cryptography|Cryptography]] does not know who you meant to pay; it only confirms that the sender is you. A typo, a clipboard malware swap, address poisoning – all of these enter the signature as your explicit consent.
  • Setting [[gloss:gas|gas]] limit to 500,000 instead of 200,000 does not make the [[gloss:transaction|transaction]] more expensive. The network charges only the [[gloss:gas|gas]] actually consumed (gasUsed); the limit is a safeguard against runaway loops. Price lives in different fields: maxFeePerGas and maxPriorityFee. Confusing "[[gloss:gas|gas]] limit" with "[[gloss:gas|gas]] price" is the most common reason people overpay – they're tuning the wrong knob.
  • The [[gloss:wallet|wallet]] signs the [[gloss:transaction|transaction]] against the network state right now: prices, pool reserves, baseFee. But it lands in a [[gloss:block|block]] 12 seconds later, sometimes more, and by then the state has moved. That gap is where you pay slippage, where sandwich bots squeeze in (see §3), where [[gloss:transaction|transactions]] revert from out-of-[[gloss:gas|gas]] as the price ticks up. The signature is valid; the world is different. :::

:::details{badge=Advanced title="ABI and the function selector: how a function name becomes 4 bytes"} :::p{html} When you call a contract function, the [[gloss:wallet|wallet]] builds the data field according to the ABI (Application Binary Interface), the standard that defines how function names and parameter values become bytes. The first 4 bytes of data are the function selector: the first 4 bytes of the keccak256 [[gloss:hash|hash]] of the function signature. A signature is the function name plus its argument types with no spaces: swapExactTokensForTokens(uint256,uint256,address[],address,uint256). [[gloss:hash|Hash]] it with keccak256, take the first 4 bytes, and you get 0x38ed1739. The parameters follow, each padded to exactly 32 bytes (types either fit into 32 bytes or point to a tail section for dynamic arrays). :::

:::p{html} Etherscan, Tenderly, and Rabby maintain databases of known selectors. Seeing 0x38ed1739, they recognize the Uniswap V2 Router function and decode its arguments. When the selector is unknown, the [[gloss:wallet|wallet]] shows raw bytes – the first red flag that either the contract is brand new or someone is trying to hide from decoders. ::: :::

:::details{badge=Advanced title="How the transaction format evolved: from legacy to EIP-7702"} :::p{html} [[gloss:eip|EIP]]-2718 introduced an "envelope" for different [[gloss:transaction|transaction]] types. Every [[gloss:transaction|TX]] starts with a type byte, and the parser knows how to read the rest: :::

:::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} All five types coexist in the network today. The [[gloss:wallet|wallet]] picks the right type based on context, and you don't usually notice – but when a [[gloss:transaction|transaction]] "won't go through" an old RPC, or an explorer shows "unknown type", the explanation is usually here. :::

:::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).
::: :::

:::quiz{id=q-3-3-s1 correct=1 explanation="Correct. The signature commits to all nine fields, including [[gloss:chainid|chainId]]. Change one byte, and the [[gloss:hash|hash]] changes – making the old signature invalid against the new [[gloss:hash|hash]]. This is the replay protection introduced by [[gloss:eip|EIP]]-155 back in 2016. The fix is to switch the [[gloss:wallet|wallet]] to [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] and sign again: a new [[gloss:chainid|chainId]], a new [[gloss:nonce|nonce]] ([[gloss:mainnet|mainnet]] keeps a separate counter for your address), and the current [[gloss:mainnet|mainnet]] baseFee."} You signed a [[gloss:transaction|transaction]] to send 0.1 [[gloss:ethereum|ETH]] to a friend, but didn't notice the [[gloss:wallet|wallet]] was switched to Arbitrum while your friend is waiting on [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]]. Can the same signature be replayed on [[gloss:mainnet|mainnet]] to avoid signing again?

  • Yes, a signature is valid on any network with the same recipient address
  • No, because [[gloss:chainid|chainId]] is part of the signed [[gloss:hash|hash]], and a different network needs a new signature
  • Yes, but only if the [[gloss:nonce|nonce]] matches on both networks
  • No, because Arbitrum uses a different [[gloss:ecdsa|ECDSA]] signature format :::

:::section{mins=4} The Price of Inclusion :::

:::lead [[gloss:gas|Gas]] isn't dollars or [[gloss:ethereum|ETH]]. It's a unit of work every computer on the network performs while executing your [[gloss:transaction|transaction]]. The instruction decides how much work; the auction for [[gloss:block|block]] space right now decides how much each unit costs. In May 2026, a typical Uniswap swap costs about 13 cents. In May 2022, the same action cost $50–$100. How to handle this as a user today is what this section is about. :::

:::p{html} The mechanics of [[gloss:gas|gas]] – [[gloss:opcode|opcodes]], [[gloss:eip|EIP]]-1559, the burn – are covered in detail in 2.2 §3. This section is about something else: when you press "Confirm", the [[gloss:wallet|wallet]] shows you an estimated cost and three fields under the hood – gasLimit, maxFeePerGas, maxPriorityFee. What each number actually means in that window, and why "estimate $0.15" is a forecast rather than a price, is the topic of §2. The baseFee on [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] currently floats around 0.4 Gwei, 50–100 times cheaper than the 2021–2022 peaks. [[gloss:ethereum|ETH]] is around $2,120. Those numbers feed the concrete figures in the window. :::

:::p{html}

Action[[gloss:gas|Gas]][[gloss:ethereum|Ethereum]] [[gloss:l1|L1]]Base [[gloss:layer 2|L2]]
[[gloss:ethereum|ETH]] transfer21,000~$0.02<$0.001
USDC transfer~65,000~$0.06<$0.001
Approve~46,000~$0.04<$0.001
Uniswap V2 swap~150,000~$0.13~$0.001
Aave deposit~250,000~$0.21~$0.002
[[gloss:nft|NFT]] mint~100,000~$0.08~$0.001
:::

:::p{html} The confirmation window shows "estimated fee ~$0.13", but the real bill is computed after the [[gloss:transaction|transaction]] lands, using (baseFee + priorityFee) × gasUsed. If your maxFeePerGas is higher than the final price, the difference doesn't get charged or burned – it was simply unused. If lower, the [[gloss:transaction|transaction]] sits in the [[gloss:mempool|mempool]] waiting for baseFee to fall back into range (see §6 on stuck [[gloss:transaction|transactions]]). And if gasUsed at execution time exceeds the gasLimit you set, the [[gloss:transaction|transaction]] reverts out-of-[[gloss:gas|gas]] – and the network still charges for the steps that ran. The estimate is a simulation against the state "right now", and 12 seconds later the state is different (see §1 KP4). :::

:::callout{icon=💡} maxFeePerGas works like a limit order on an exchange. You tell the broker "buy [[gloss:ethereum|ETH]] at no more than $2,100". If the market opens at $2,050, you pay $2,050, not $2,100. The difference doesn't leave your account and doesn't go anywhere – it's just the line you wouldn't cross. If the market opens at $2,200, the order doesn't fill, and you wait for the price to drop back into your range. Same with [[gloss:gas|gas]]: the [[gloss:wallet|wallet]] sets a ceiling at 2–3× the current baseFee, and that's fine. The buffer doesn't get burned – it just means "I'll pay that much if I have to, but I probably won't have to". If a [[gloss:transaction|transaction]] "gets stuck", chances are baseFee spiked past your ceiling, exactly the way a limit order misses when the market runs away. :::

:::keypoints{label="Key Points"}

  • A common misread: setting gasLimit to 300,000 doesn't mean "you can't be charged for more than that in dollars". gasLimit is a cap on the amount of work, not on your spending. If the contract uses 180,000 [[gloss:gas|gas]], you pay for 180,000 × current price – not for 300,000. The cap exists to protect you from runaway loops or contracts that turned out to be more expensive than expected. Setting it too low causes out-of-[[gloss:gas|gas]] revert; setting it generously is safe and doesn't cost you anything extra.
  • "Cancel" in the [[gloss:wallet|wallet]] isn't a revoke. It's a new [[gloss:transaction|transaction]] with the same [[gloss:nonce|nonce]] and a higher [[gloss:gas|gas]] price, sent so it gets included instead of the original. That second [[gloss:transaction|transaction]] goes through the network and costs [[gloss:gas|gas]] too – at minimum 21,000 for an empty transfer, and you pay the current rate for it. Speed-up works the same way: another [[gloss:transaction|TX]], another fee. Changing your mind in [[gloss:ethereum|Ethereum]] is never free – more in §6.
  • baseFee isn't yours to set – it's computed by the algorithm. gasUsed isn't yours either – it's fixed by the contract's code. The only knob for inclusion speed is priorityFee (the [[gloss:validator|validator]]'s tip). For a normal [[gloss:transaction|transaction]], 0.01–0.05 Gwei is enough; for an urgent one, 0.5–1 Gwei; for a critical one, 2–5 Gwei. Most overpaying happens when users crank the tip way up without needing to.
  • All three parameters (gasLimit, maxFeePerGas, maxPriorityFee) work the same way on Arbitrum, Base, and Optimism as on [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]]. But baseFee there isn't measured in tenths of a Gwei – it's measured in ten-thousandths, and a typical [[gloss:transaction|transaction]] costs fractions of a cent rather than cents. From the user's side, the buffer doesn't really cost anything, and overpaying on [[gloss:layer 2|L2]] is essentially impossible. Detailed [[gloss:layer 2|L2]] pricing in §7. :::

:::details{badge=Practice title="Setting priorityFee without overpaying"} :::p{html} The tip is the only knob you actually control for inclusion speed. Concrete ranges for May 2026: :::

:::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} Where to read live prices: :::

:::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} A typical cushion against sudden jumps: maxFeePerGas = baseFee × 2 + priorityFee. If the baseFee spikes at the moment of inclusion, you have a buffer. If it doesn't, only baseFee + priorityFee is charged. The buffer doesn't get burned. The cushion is insurance, not prepayment. :::

:::p{html}

  • Не-срочная транзакция (перевод другу, обычный swap не в момент пампа): 0,01-0,05 Gwei. Это копейки.
  • Срочная (поймать арбитраж, мини-[[gloss:mev|MEV]]-конкуренция): 0,5-1 Gwei.
  • Критическая (ликвидация позиции, подпись во время волатильного движения): 2-5 Gwei.
:::

:::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
::: :::

:::quiz{id=q-3-3-s2 correct=2 explanation="Correct. maxFeePerGas is a ceiling, not the price. The real bill is (baseFee + priorityFee) × gasUsed. If your ceiling is higher than the bill, the difference doesn't get charged or burned. If it's lower, the [[gloss:transaction|transaction]] waits. The estimate uses the current baseFee, your priorityFee, and the predicted gasUsed – it's close to reality but not exact, because the network state moves a little by the time the [[gloss:transaction|transaction]] actually lands. The cushion on maxFeePerGas is insurance, not a prepayment."} Your [[gloss:wallet|wallet]] shows "estimated fee ~$0.15", you set maxFeePerGas at double the current baseFee, and the [[gloss:transaction|transaction]] lands without delay. What did you actually pay?

  • About $0.30, because the protocol charges exactly the maxFeePerGas you set, which was twice the estimated base fee
  • Exactly $0.15, because the protocol guarantees the [[gloss:wallet|wallet]] estimate is the final amount charged
  • Around $0.15, give or take – what's actually charged is (baseFee + priorityFee) × gasUsed, the ceiling isn't charged
  • Exactly maxFeePerGas × gasLimit – the maximum possible :::

:::section{mins=6} Mempool and MEV :::

:::lead Over the past year, DEX users have lost roughly $60 million to one specific kind of attack that most of them don't know exists. It's not a hack, not a phishing scam, not a [[gloss:wallet|wallet]] bug. It's normal [[gloss:ethereum|Ethereum]] operation multiplied by bots that read your [[gloss:transaction|transaction]] before it lands in a [[gloss:block|block]]. The headline case from March 2025: a user tried to swap 220,000 USDC for USDT and received $5,000. 98% of the trade went to an automated program in a single [[gloss:block|block]]. :::

:::p{html} When you press Confirm and the [[gloss:wallet|wallet]] sends a signed [[gloss:transaction|transaction]] into the network, it doesn't go straight to a [[gloss:validator|validator]]. It hits your RPC [[gloss:node|node]] (see 1.2 §6), which forwards it to other [[gloss:node|nodes]] via the P2P protocol. This stage is called the [[gloss:mempool|mempool]] – a shared "waiting room" where [[gloss:transaction|transactions]] sit until a [[gloss:validator|validator]] picks them for the next [[gloss:block|block]]. The critical detail: the [[gloss:mempool|mempool]] is public. Anyone running an [[gloss:ethereum|Ethereum]] [[gloss:node|node]], or subscribing to a service like Blocknative, sees your [[gloss:transaction|transaction]] in full – to, data, value, maxFeePerGas – seconds before it executes. This is not a leak or a bug. It's the basic design of a P2P network: the faster a [[gloss:transaction|transaction]] propagates, the faster it gets included. :::

:::p{html} Say you're swapping 5,000 USDC for [[gloss:ethereum|ETH]] on Uniswap V2 with a slippage tolerance of 5%. A bot reading the [[gloss:mempool|mempool]] sees your [[gloss:transaction|transaction]] and simulates it on its own [[gloss:node|node]] in 50 milliseconds. It concludes: "this buy will push the pool's [[gloss:ethereum|ETH]] price up by 0.3%". Then the bot sends two [[gloss:transaction|transactions]] with a higher priorityFee: the first (front-run) lands before yours and buys [[gloss:ethereum|ETH]] cheap; the second (back-run) lands after yours and sells the same [[gloss:ethereum|ETH]] at the price you just pushed up. The [[gloss:validator|validator]] orders the [[gloss:block|block]] by tips, so the bot's buy goes first, your swap executes at the now-shifted price (you get less [[gloss:ethereum|ETH]] than you expected), and the bot's sell closes the trade with a profit. The specific case from March 12, 2025: a user sent a swap for 220,764 USDC → USDT with slippage cranked to the max, the bot extracted everything it could, and the user received 5,271 USDT. 98% of the trade went to the front-run/back-run pair. :::

:::p{html} In 2023–2024, this was the searcher market – independent bot operators hunting for opportunities in the [[gloss:mempool|mempool]]. Today, around 70% of all sandwiches are run by a single player known as jaredfromsubway.[[gloss:ethereum|eth]]: a bot that has processed more than 3 million [[gloss:transaction|transactions]] by 2025 and earned $40 million since 2023. But Jared isn't alone – there's a whole pyramid of roles: searchers spot the opportunity, builders assemble [[gloss:block|blocks]] for maximum profit, relays pass [[gloss:block|blocks]] to [[gloss:validator|validators]] through [[gloss:mev|MEV]]-Boost, and the proposer ([[gloss:validator|validator]]) includes the [[gloss:block|block]] and takes their share. About 90% of [[gloss:ethereum|Ethereum]] [[gloss:block|blocks]] today are built through this system. This isn't "villains breaking the rules" – it's legal financial infrastructure that exists because [[gloss:validator|validators]] find it profitable to outsource [[gloss:block|block]]-building to specialists for a cut. :::

:::p{html} There's one main defense: don't send your [[gloss:transaction|transaction]] to the public [[gloss:mempool|mempool]]. Three practical ways to do this: :::

:::p{html}

  • [[gloss:flashbots|Flashbots]] Protect – an RPC endpoint (https://rpc.[[gloss:flashbots|flashbots]].net) that drops your [[gloss:transaction|transaction]] into a private pool and hands it directly to a builder for [[gloss:block|block]] inclusion, with no broadcast. Bots never see it. Free, added to the [[gloss:wallet|wallet]] like any other network.
  • [[gloss:mev|MEV]] Blocker (https://rpc.mevblocker.io) – the same as [[gloss:flashbots|Flashbots]] Protect, plus it refunds 90% of any extracted [[gloss:mev|MEV]] back to the user as a kickback. In practice this turns any incidental [[gloss:mev|MEV]] into a small profit for you.
  • CowSwap, 1inch Fusion – RFQ systems: you don't send a regular swap, you publish an "intent", and professional solvers compete to fill your trade. A sandwich has no point of entry here.
:::

:::p{html} Alongside any of the three: slippage tolerance is a budget you hand the bot. 0.5% protects you far better than 5%. Many [[gloss:wallet|wallets]] default to 1–3% – override that manually on large amounts. :::

:::callout{icon=💡} A poker table where your cards look normal to you, but three pros at the table are wearing X-ray glasses. You place a bet: they already know your hand, and they see your bet a moment before it lands on the table. One of them has the right to play before you (they paid more for entry), and they instantly raise. By the time your turn comes around, the pot's price has shifted. Right after your [[gloss:move|move]], the same pro locks in the profit. You're technically playing by the rules – no one swapped your cards. You were just outplayed by an information advantage that, for the other players, is completely legal. The only way to play fair is to find a table where the cards stay opaque (those are private RPCs), or a table with different rules entirely, where order isn't determined by speed of reaction (those are RFQ systems like CowSwap). :::

:::keypoints{label="Key Points"}

  • MetaMask, Rabby, Trust – every major [[gloss:wallet|wallet]] broadcasts to the public pool by default, because that's the route that propagates fastest. It's not "the [[gloss:wallet|wallet]] sells your data", it's how the [[gloss:ethereum|Ethereum]] P2P network is built: every [[gloss:node|node]] forwards your bytes onward until everyone sees them. To become invisible to bots, you have to explicitly switch the [[gloss:wallet|wallet]] to a private RPC like [[gloss:flashbots|Flashbots]] Protect or [[gloss:mev|MEV]] Blocker.
  • Most users read "5% slippage" as "I agree to lose up to 5% from market [[gloss:movement|movement]]". The real meaning is different: "a bot can extract up to 5% from me, if it can". At 5% slippage, the bot takes exactly 5%, not "a small price [[gloss:movement|movement]]". For large swaps, always set 0.1–0.5% manually – don't trust the [[gloss:wallet|wallet]]'s "auto".
  • Counter-intuitive: a USDC↔USDT swap shouldn't attract bots – the price doesn't [[gloss:move|move]]. But 30% of all sandwich attacks target [[gloss:stablecoin|stablecoin]] pairs. The reason is the AMM formula itself: the in-pool price shifts with the size of the trade, not with the "real" market price. The larger the stable amount, the more the bot extracts. The March 2025 $220k case was exactly that: USDC → USDT, not some exotic pair.
  • Searchers, builders, relays, proposer – a pyramid of roles that assembles ~90% of [[gloss:ethereum|Ethereum]] [[gloss:block|blocks]] through [[gloss:mev|MEV]]-Boost. These are legal roles, regulated by market competition: builders compete for the highest profit per [[gloss:block|block]], each layer earns on its slice. [[gloss:mev|MEV]] won't be "fixed" by a protocol upgrade, because there's nothing to fix – the system works as designed. The defense lives entirely on the sender's side. :::

:::details{badge=Practice title="Switching to Flashbots Protect or MEV Blocker"} :::p{html} Technically this takes 60 seconds. In MetaMask: :::

:::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} Rabby is the same flow under Settings → RPC. Trust [[gloss:wallet|Wallet]] under Manage Networks. :::

:::p{html} Once switched, every [[gloss:transaction|transaction]] goes straight to a builder, bypassing the public [[gloss:mempool|mempool]]. Bots simply don't see it. Trade-off: inclusion latency may be 1–2 [[gloss:block|blocks]] longer (12–24 seconds), because the builder waits for the optimal moment. In practice, you don't notice. :::

:::p{html} When to definitely switch: on any swap over $1,000, on [[gloss:permit|permit]]/permit2 signatures (see §5), on operations involving newly-launched pools with thin liquidity. For micro-[[gloss:transaction|transactions]] ($20–$100), the regular [[gloss:mempool|mempool]] is usually fine – a bot isn't economically motivated to target an amount that small. :::

:::p{html} CowSwap and 1inch Fusion work differently – you don't change the RPC, you just use their interface instead of regular Uniswap. Trades execute through RFQ, and bots have no entry point in that architecture. :::

:::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
  • Сохранить, переключиться на новую сеть в выпадающем списке
::: :::

:::details{badge=Advanced title="The MEV pyramid and the Peraire-Bueno case"} :::p{html} [[gloss:ethereum|Ethereum]] runs through Proposer-Builder Separation (PBS), implemented in practice through [[gloss:mev|MEV]]-Boost. The roles are strictly separated: :::

:::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} Around 90% of [[gloss:ethereum|Ethereum]] [[gloss:block|blocks]] go through this chain. Profit splits between builders (~5–10% per [[gloss:block|block]]), searchers (the right to include their bundles), and the proposer (paid by the builder). :::

:::p{html} The Peraire-Bueno case (May 2024). Two MIT graduate brothers – Anton and James Peraire-Bueno – found a vulnerability in a [[gloss:mev|MEV]]-Boost relay implementation: under a specific sequence of actions, they could reveal the contents of the [[gloss:block|block]] before the proposer's signature and rewrite [[gloss:transaction|transactions]] inside the bundle. In a single [[gloss:block|block]] (12 seconds), they extracted about $25 million, redirecting [[gloss:transaction|transactions]] in their favor. The US DOJ filed federal charges in 2024 for wire fraud and money laundering. This is the first precedent in which [[gloss:mev|MEV]] extraction was treated as a criminal offense – not [[gloss:mev|MEV]] itself (still legal), but manipulation of the relay infrastructure. The case is ongoing. :::

:::p{html} Running in parallel: an operator under the name Sandwich The Ripper extracted around $20M through similar relay manipulations. This shows something important: even the [[gloss:mev|MEV]] infrastructure itself isn't "safe" – it gets attacked from within. :::

:::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, подписывает, получает свою долю
::: :::

:::quiz{id=q-3-3-s3 correct=2 explanation="Correct. A bot in the public [[gloss:mempool|mempool]] sees your [[gloss:transaction|transaction]] before the [[gloss:validator|validator]] does and can always outbid your tip – so priorityFee (A) doesn't help; bots have more of that resource than you do. A low maxFeePerGas (D) just adds delay; invisibility through that route isn't possible. Lowering slippage to 0.3% (B) genuinely limits the bot's take – it's a useful measure, but it narrows the attack rather than blocking it. The only thing that fully protects you is sending the [[gloss:transaction|transaction]] outside the public [[gloss:mempool|mempool]] through [[gloss:flashbots|Flashbots]] Protect or [[gloss:mev|MEV]] Blocker (C). The right practice on large swaps is a combination: switch to a private RPC and set slippage to 0.3–0.5%."} You're swapping 5,000 USDC for [[gloss:ethereum|ETH]] on Uniswap through Rabby. What protects you best from a sandwich attack?

  • Set priorityFee high (5 Gwei) to get included first
  • Lower slippage tolerance to 0.3% instead of the default 1%
  • Switch the [[gloss:wallet|wallet]]'s RPC to [[gloss:flashbots|Flashbots]] Protect
  • Lower maxFeePerGas to stay invisible to bots :::

:::section{mins=7} What You're Actually Signing :::

:::lead A signature in Web3 comes in four different forms. One is deprecated and modern [[gloss:wallet|wallets]] flag it as dangerous. Another looks like plain text and is used for logins – it's safe. The third is the standard [[gloss:transaction|transaction]] we covered earlier. The fourth – the [[gloss:eip-712|EIP-712]] structured signature – looks readable, but it's exactly the one attackers learned to exploit. In February 2025, this fourth type cost Bybit $1.5 billion in a single signature. :::

:::p{html} When the [[gloss:wallet|wallet]] pops up to confirm something, under the hood it uses one of four standard methods. Each does something different, and each carries a different risk. :::

:::p{html}

  • eth_sendTransaction – a standard [[gloss:transaction|transaction]], the nine-field structure we covered (see §1). The [[gloss:wallet|wallet]] decodes the fields, the hardware [[gloss:wallet|wallet]] decodes them too. This is the most legible kind of signature.
  • eth_sign – signs an arbitrary 32-byte [[gloss:hash|hash]] with no structure attached. Nobody knows what's inside the [[gloss:hash|hash]]: it could be a [[gloss:transaction|transaction]], it could be anything else. The method is officially deprecated, and modern [[gloss:wallet|wallets]] (MetaMask, Rabby) refuse to show it or warn with a red banner. If a dApp asks for eth_sign – that's a red flag.
  • personal_sign – signs a text message with a mandatory prefix, "[[gloss:ethereum|Ethereum]] Signed Message". The prefix guarantees one thing: this signature cannot be replayed as a [[gloss:transaction|transaction]] signature. Used for dApp login ("Sign In With [[gloss:ethereum|Ethereum]]"), for off-chain proofs of address ownership. Hardware [[gloss:wallet|wallets]] show the readable text. This is safe by design.
  • eth_signTypedData ([[gloss:eip-712|EIP-712]]) – a structured signature. Not text, but a typed structure: domain (which dApp), types (the field schema), message (the actual content). If the [[gloss:wallet|wallet]] knows the schema, it displays readable: "[[gloss:permit|Permit]] USDC, 1000 tokens, for contract 0x...". Used for [[gloss:permit|permit]]/permit2 (see §5), off-chain orders on [[gloss:nft|NFT]] marketplaces, DAO votes. A useful standard. But it's also the one attackers learned to exploit.
:::

:::p{html} "Blind signing" is any case where you press Approve without understanding what exactly your signature commits to. In practice this happens when the [[gloss:wallet|wallet]], or the hardware [[gloss:wallet|wallet]], fails to decode the structure and shows only a [[gloss:hash|hash]] like 0x3a7f9d...c2. The [[gloss:hash|hash]] itself carries no meaning – it's just a fingerprint of what's inside. If you don't know what's inside, you're signing "something" and trusting whoever produced that something. Most of the time, the "producer" is the dApp interface in your browser. This is where the trust chain breaks: between the dApp's website and your signature stands JavaScript running inside your browser. A compromised CDN, an XSS, an injected script – any of these can change what gets sent to the [[gloss:wallet|wallet]], while the page still looks fine. That's the architecture of the Bybit incident. :::

:::p{html} The textbook example of where this trust chain breaks is the Bybit hack of February 21, 2025: three signers of a 3-of-6 Safe [[gloss:multisig|multisig]] approved what their interface showed as a routine 30,000 [[gloss:ethereum|ETH]] transfer, while the Ledger devices were displaying only the [[gloss:hash|hash]] of an [[gloss:eip-712|EIP-712]] structure they couldn't decode. The actual structure called delegatecall on the attackers' contract, which rewrote Safe's implementation and let them drain 401,347 [[gloss:ethereum|ETH]] – about $1.46 billion. The full step-by-step of how the Safe UI was compromised and what each parameter looked like on screen is in 3.2 §2, including a simulator where you can sit in the signer's chair. For §4 the lesson is narrow: three competent people with hardware [[gloss:wallet|wallets]] blind-signed an [[gloss:eip-712|EIP-712]] structure because the [[gloss:wallet|wallet]] couldn't decode it. The chain of trust failed at the layer between the dApp UI and the device – exactly where this section says it can fail. :::

:::p{html} The principle is one: never sign a [[gloss:hash|hash]] you don't understand. In practice that means several concrete things: :::

:::p{html}

  • Use [[gloss:wallet|wallets]] with pre-sign simulation. Rabby shows the [[gloss:transaction|transaction]]'s result before signing: "you receive X, you give Y, contract balance Z changes like this". MetaMask does the same through Blockaid. If the simulation looks strange or shows nothing – cancel.
  • For large operations, use a hardware [[gloss:wallet|wallet]] with a large screen. Ledger Stax/Flex and Trezor Safe 5 decode [[gloss:eip-712|EIP-712]] independently of the PC. If the screen shows "delegatecall to 0xXXX" while the Safe interface in the browser shows "regular transfer" – that's the signal not to sign.
  • For very large operations – Tenderly or another simulator. Paste the [[gloss:transaction|transaction]] [[gloss:hash|hash]] into Tenderly Dashboard, walk through what will happen step by step. It takes a minute and gives you an independent view.
  • Never sign eth_sign. Not "cautiously" – just never. Modern dApps don't use it.
:::

:::p{html} As of May 2026, the industry is rolling out a standard called [[gloss:eip|EIP]]-7730 – descriptions of [[gloss:eip-712|EIP-712]] schemas in a format hardware [[gloss:wallet|wallets]] can read on their own. This turns the current default "blind signing" into clear signing for most dApps. The standard isn't everywhere yet, so for now the burden sits on the user. :::

:::callout{icon=💡} The admissions room of a hospital. Two stacks of paper sit in front of you. The first is a detailed form ten pages long: what they're going to do, where the incision goes, on which side, under what anaesthesia, what the risks are. You can read it, ask the doctor questions, refuse, request a second opinion. The second stack has one sheet with a single line and a place to sign: "I consent to medical treatment". Legally, both signatures are equal. But the first is informed consent – you know what you're agreeing to. The second is blank consent. If the OR team decides to do something else, your signature on the blank form is enough. A modern Ledger Stax is the "first stack" – it shows you the detailed form. A [[gloss:hash|hash]] on the small screen of a Ledger Nano is the "second stack", with just "I consent" on it. The signature is the same. The understanding is not. :::

:::keypoints{label="Key Points"}

  • An off-chain signature isn't a one-time event. A [[gloss:permit|permit]] signed today, with no deadline field or a deadline set far in the future, stays valid until you revoke the on-chain allowance (via revoke.cash or the token contract directly). The attacker doesn't need to use it immediately – they store it and trigger whenever it's profitable, weeks or months later. The instinct "I signed that ages ago, it's probably expired" is wrong: signatures don't expire on their own.
  • A software [[gloss:wallet|wallet]] (MetaMask, Rabby) is a browser extension. The dApp's website is also in the browser. They share the same process, the same storage, the same compromised browser if it's compromised. On an infected machine, the "[[gloss:wallet|wallet]]" and the "dApp" are not separate parties – they're the same software stack with different labels. Real isolation only comes from a hardware [[gloss:wallet|wallet]] on a physically separate device with its own screen.
  • A clean decoded signature is not the same as a safe one. Drainer kits in 2025 write technically valid permits that the [[gloss:wallet|wallet]] renders neatly: "approve USDC, $50, to 0xCAFE…". The [[gloss:wallet|wallet]] is showing the truth – that's literally what's about to happen, including the part where 0xCAFE empties your USDC. The signal you actually need is not "did this decode" but "do I recognize the spender?" and "is value the amount I expected, not unlimited?"
  • "Safe by design" doesn't mean "zero risk". The prefix in personal_sign [[gloss:block|blocks]] reuse as a [[gloss:transaction|transaction]], but a phishing site that asks you to "sign in" can capture your signature and log in as you on any dApp using Sign In With [[gloss:ethereum|Ethereum]] (SIWE). On Hyperliquid, dYdX, and similar venues, off-chain orders are also personal_sign messages – a careless sign could hand over a trading authorization. Read the text every time, even when the format looks harmless. :::

:::details{badge=Practice title="A checklist before signing EIP-712"} :::p{html} When the [[gloss:wallet|wallet]] shows a "typed data" signature request ([[gloss:eip-712|EIP-712]]), walk through this list before pressing Confirm: :::

:::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} For large operations there's a tool called Tenderly Dashboard (tenderly.co). Paste the signature parameters or the [[gloss:transaction|transaction]] [[gloss:hash|hash]], run a simulation, walk through the execution trace. It gives you an independent view of what will happen after you sign – without trusting the dApp frontend. :::

:::p{html} For [[gloss:permit|permit]] deals (see §5), additionally check two fields: spender (who gets the right to spend your tokens) and value (how much). An infinite allowance (a number like 115792089237316195423570985008687907853269984665640564039457584007913129639935) is a classic drainer signature. No legitimate dApp asks for an unlimited [[gloss:permit|permit]] for a one-off operation. :::

:::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.
::: :::

:::quiz{id=q-3-3-s4 correct=1 explanation="Correct. eth_sign is deprecated and shouldn't appear in 2026 at all. The method signs an arbitrary 32-byte [[gloss:hash|hash]] with no decoding – behind that [[gloss:hash|hash]] could be a [[gloss:transaction|transaction]] signature that drains your funds. personal_sign (A) is safe by design: the "[[gloss:ethereum|Ethereum]] Signed Message" prefix guarantees that the signature cannot be replayed as a [[gloss:transaction|transaction]]. eth_sendTransaction with decoded fields (C) is a standard [[gloss:transaction|transaction]]; the risk is only in whether you read what's there. eth_signTypedData with visible parameters (D) needs a domain-and-message check, but it's a normal modern method. Seeing eth_sign is the main red flag – cancel and figure out what the dApp is trying to do."} You open a [[gloss:defi|DeFi]] site that asks you to sign a message. The [[gloss:wallet|wallet]] shows a Confirm button and the request type. Which of these requests definitely needs extra caution?

  • personal_sign with text "Sign in to Aave v3"
  • eth_sign, a request to sign [[gloss:hash|hash]] 0x4a8c2d...
  • eth_sendTransaction for a swap of 100 USDC → [[gloss:ethereum|ETH]] with decoded fields
  • eth_signTypedData for a [[gloss:permit|permit]] of $50 USDC with visible parameters :::

:::section{mins=8} Approve and Permit :::

:::lead In 2024, DEX and [[gloss:defi|DeFi]] users lost $494 million to one specific kind of signature. Not a hack, not a leaked [[gloss:seed phrase|seed phrase]] – a signature for "approve" or "[[gloss:permit|permit]]" that they placed themselves on a phishing site. In 2025 the figure dropped to $84 million, not because the attack disappeared but because fewer people sit in [[gloss:defi|DeFi]] and more [[gloss:wallet|wallets]] learned to [[gloss:block|block]] the most dangerous formats. That signature grants a contract permission to spend your tokens. If the permission is unlimited, it sits there forever. :::

:::p{html} The first time you swap USDC for [[gloss:ethereum|ETH]] on Uniswap, two [[gloss:transaction|transactions]] happen, not one. First, approve(spender=Uniswap, amount=X) – a write to USDC's storage that says: "the contract at address Uniswap may spend X of my USDC". This entry is called an allowance, and it stays in storage forever unless you submit a new approve for 0. Only after the first [[gloss:transaction|transaction]] can the second happen – the swap itself, during which Uniswap calls transferFrom and actually moves your USDC. Most users only remember the swap, because "the [[gloss:ethereum|ETH]] showed up right after the click". But the first [[gloss:transaction|transaction]] was approve, and its result – the allowance – sits forever, in most cases as unlimited (2^256 − 1): [[gloss:wallet|wallets]] and dApps default to an infinite amount so you don't have to approve every time. As of January 2026, the average [[gloss:defi|DeFi]] user holds 40–60 active allowances across different contracts, forgotten, never revoked. :::

:::p{html} In 2020, the [[gloss:permit|EIP-2612]] standard appeared: permission could be granted not through an on-chain [[gloss:transaction|transaction]] but through an off-chain signature. The structure is [[gloss:eip-712|EIP-712]] (see §4): domain (the token address), types (the [[gloss:permit|Permit]] fields), message (spender, value, deadline, [[gloss:nonce|nonce]]). You sign that structure in your [[gloss:wallet|wallet]] (no [[gloss:gas|gas]]), pass it to the spender, and the spender calls [[gloss:permit|permit]] + transferFrom in a single [[gloss:transaction|transaction]]. On the token's side this is equivalent to approve plus the allowance being spent – but without two separate [[gloss:transaction|transactions]]. USDC supports [[gloss:permit|permit]] since 2020, DAI from the start, many modern ERC-20s do too. It's a convenient pattern: one click instead of two, the [[gloss:gas|gas]] cost of approve saved. But the signature lives with the recipient, and until the deadline expires, it can be used at any moment. :::

:::p{html} In August 2022, Uniswap published the Permit2 contract at 0x000000000022D473030F116dDEE9F6B43aC78BA3 (the address is part of the brand – chosen deliberately via CREATE2). The idea: instead of every token implementing its own [[gloss:permit|permit]], there's one universal intermediary contract. You do one normal approve(Permit2, unlimited) for each of your tokens (USDC, USDT, DAI, WBTC, everything). After that, any dApp supporting Permit2 accepts short signatures from you: "let Permit2 spend X of my USDC for contract Y". Permit2 verifies the signature and calls transferFrom. No separate approve is needed for any new dApp – they all route through Permit2. As of May 2026, Permit2 is a shared layer underneath most DEXs and aggregators: Uniswap V3+, 1inch, Cowswap, NEAR Intents. The convenience is enormous. But there's a catch: a single Permit2 signature is a permission for any token that Permit2 holds an allowance for. Drainers learned to play exactly this hand – 56.7% of all phishing attacks in 2024 went through Permit2. :::

:::p{html} By the end of 2024, drainers had matured into a full industry with its own economy. The main players: :::

:::p{html}

  • Inferno Drainer: drainer-as-a-service with a 20% cut. Around $80 million stolen across 2023–2024. Announced an exit in Q4 2024, sold infrastructure to Angel Drainer. Check Point Research shows operations continuing under new names.
  • Pink Drainer: 28% of the market in the first half of 2024, around $55 million. Exited in Q2 2024.
  • Angel Drainer: 42% of the market in Q1–Q2 2024, inherited Inferno's infrastructure.
  • Ace, Vanilla, Cerberus: new entrants in 2025. Vanilla earned $5 million in three weeks in August 2025.
:::

:::p{html} The main method stayed the same: a phishing site with a fake airdrop or a fake mint asks for a permit2 signature "for verification" or "for claim". The signature really is valid, it just signs the drain of every token Permit2 has an allowance for. Total losses in 2024: $494 million across 332,000 [[gloss:wallet|wallets]], with the largest single theft at $55.48 million. In 2025 the figure fell to $83.85 million across 106,000 victims. The 83% drop wasn't because the attack disappeared – MetaMask, Rabby, and other [[gloss:wallet|wallets]] began blocking suspicious permit2 signatures, dApp aggregators like Blockaid flag phishing domains in real time, and users gradually learned to read the spender field. :::

:::p{html} After [[gloss:pectra|Pectra]] activated on May 7, 2025, a new signature format appeared: [[gloss:eip-7702|EIP-7702]], which lets an [[gloss:eoa|EOA]] temporarily act as a [[gloss:smart contract|smart contract]]. The good applications: granular permissions, batch operations, [[gloss:gas|gas]] sponsorship. The bad one: an attacker hands you an [[gloss:eip-7702|EIP-7702]] signature in which your [[gloss:eoa|EOA]] temporarily "becomes" the attacker's contract, and that contract in the same [[gloss:transaction|transaction]] performs transferFrom on every token you hold an allowance against. One signature, full drain. Two large [[gloss:eip-7702|EIP-7702]] incidents occurred in August 2025 for a combined $2.54 million; as of January 2026 it's a separate category in Scam Sniffer reports – not at peak volume yet, but growing. :::

:::callout{icon=💡} A signed check handed to a vendor. An approve for a specific amount is an ordinary check: the signature is there, the amount is filled in, the vendor can walk to the bank and cash it. Signing a blank check (an unlimited approve) is permission to fill in any amount they like, whenever they like. The blank sits in the vendor's safe, not yours. To revoke it, you have to walk into the bank and freeze the account; until you do, the blank stays valid. A [[gloss:permit|permit]] is the same check, but signed remotely and mailed to the recipient – no bank visit required. A permit2 is a trusted broker: you sign one general authorization for all your accounts, and the broker then issues fast sub-checks to different vendors against your signatures. A drainer attack is a swap of the check at the moment of signing: you thought you were signing a $50 check to one store, and you actually signed a blank that the same store can cash across all your accounts at once. :::

:::keypoints{label="Key Points"}

  • If you once signed an approve for a vault that's later abandoned by its developers, the allowance in USDC's storage stays live forever. The contract isn't deleted when the website goes away – it stays on-chain and keeps every right it received. If a vulnerability is found in its code later, every unlimited allowance ever granted to that vault turns into an open door. An allowance isn't an active subscription, it's a passive record sitting in storage waiting.
  • Unlike a plain allowance which lives forever, Permit2 stores an expiration for each (token, spender) pair: after that date, the permission expires on its own. Good dApps could set expiration to an hour or a day. In practice, most set it years out, which is equivalent to infinite. If you check your allowances on revoke.cash, watch the Expires column: something like "December 31, 2099" is a red flag, even when the dApp looks familiar.
  • Drainers don't "hack Uniswap" or "hack USDC". Technically the USDC contract works perfectly: a valid [[gloss:permit|permit]] signature arrives, the contract honors it. Defense only lives at the moment of signing. The one habit that works: every time before pressing Confirm, read the spender and value fields. If the spender isn't who you expect, or the value is unlimited – don't sign.
  • These are two different channels, each with its own defense. A [[gloss:permit|permit]] attack authorizes spending a specific token; you revoke the allowance via revoke.cash and [[gloss:move|move]] funds to a new address. An [[gloss:eip-7702|EIP-7702]] attack turns your [[gloss:eoa|EOA]] into the attacker's contract for one [[gloss:transaction|transaction]]; the defense is never signing [[gloss:eip-7702|EIP-7702]] requests from unknown sites, and remembering that revoking it requires another signed [[gloss:transaction|transaction]]. :::

:::details{badge=Practice title="How to use revoke.cash"} :::p{html} Revoke.cash is the main practical tool for managing allowances. What to do: :::

:::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} Run a cleanup every 3–6 months. After a heavy [[gloss:defi|DeFi]] run (a series of airdrop claims, for example) – immediately. Cost of 10–20 revokes on [[gloss:mainnet|mainnet]]: a couple of dollars. :::

:::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]]-маркетплейсы.
::: :::

:::details{badge=Advanced title="Permit2 internals and the drainer ecosystem"} :::p{html} The internals of Permit2 are simpler than they look. The contract stores two structures: :::

:::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} The distinction matters: the first is a persistent permission with an expiry; the second is single-use. Drainers often pick the second as a cover, because it looks less suspicious ("a one-time test permission for verification"), while in fact it moves the funds immediately. :::

:::p{html} The drainer ecosystem in 2024–2025: after Inferno's exit (Q4 2024) and Pink's exit (Q2 2024), the market first consolidated under Angel Drainer (60% by the end of 2024), then fragmented. As of January 2026, the major players are Angel, Ace, Vanilla, and Cerberus. All work on a drainer-as-a-service model with a 15–20% cut. Their "clients" are ordinary scammers who need turnkey infrastructure: a phishing domain with a clone-looking interface, a ready payload, mixers for laundering. It's a full criminal economy with competition, support, and marketing. :::

:::p{html} The big shift in 2025 was the overall 83% drop in volumes – not because drainers were "beaten", but because of two factors: a general decline in [[gloss:defi|DeFi]] activity (especially after the Q2 2025 correction) and improved [[gloss:wallet|wallet]] protection. MetaMask integrated Blockaid; Rabby [[gloss:block|blocks]] suspicious Permit2 by default; Phantom and Backpack on [[gloss:solana|Solana]] added pre-sign warnings. This pushes the attack toward less technically aware users and into new formats like [[gloss:eip-7702|EIP-7702]] – the same $2.54 million from August 2025. :::

:::p{html}

  • Allowance – для каждой пары (owner, token, spender): amount, expiration, [[gloss:nonce|nonce]]. Это то, что устанавливается через подпись.
  • PermitTransferFrom – однократное разрешение для одной операции, без накопления allowance.
::: :::

:::quiz{id=q-3-3-s5 correct=2 explanation="Correct. The only option that explains the drain of USDC and USDT, DAI, WBTC at once. Any swap on Uniswap V3 routes through Permit2 (contract 0x000000000022D473030F116dDEE9F6B43aC78BA3), and Uniswap by default requests approve(Permit2, unlimited) for every token that may be needed in the deal. A year later you've forgotten about it, but Permit2 holds an unlimited allowance on your USDC and likely on the [[gloss:ethereum|ETH]]-pair tokens around it – USDT and WBTC, if you traded them. The "[[gloss:permit|permit]] on $50 USDC" signature on the phishing site was actually a Permit2 signature authorizing a drain of every token Permit2 can touch. A doesn't work: seed phrases don't "leak through cookies". B – an attack on USDC alone doesn't explain the other tokens. D – the USDC contract isn't compromised, it did exactly what it was designed to do."} You made one swap, USDC for [[gloss:ethereum|ETH]] on Uniswap V3, in February 2024. A year later, in February 2025, you land on a new airdrop-claim site that asks for a "[[gloss:permit|permit]] signature on $50 USDC" for verification. You sign. The next day you see that not only your USDC, but also all your USDT, DAI, and WBTC have been drained. How do you explain this?

  • The phishing site got access to your [[gloss:seed phrase|seed phrase]] through cookies
  • The [[gloss:permit|permit]] signature on $50 USDC gave the attacker an unlimited allowance specifically on USDC
  • The signature went through Permit2, which already had an unlimited allowance on all your tokens from the old Uniswap V3 swap
  • The attacker exploited the USDC contract and drained all users' funds :::

:::section{mins=7} When Transactions Break :::

:::lead Everyone who's used [[gloss:defi|DeFi]] has seen the red "[[gloss:transaction|Transaction]] failed" banner. The [[gloss:gas|gas]] is gone, the action didn't happen, the [[gloss:wallet|wallet]] shows an opaque error code. Or another scenario: the [[gloss:transaction|TX]] is sent but sits at "pending" for twenty minutes. Or worse: you sent several TXs in a row, and now they're all stuck. Each of these has a specific name and a specific fix. :::

:::p{html} A revert goes like this: your [[gloss:transaction|transaction]] lands in a [[gloss:block|block]] (the [[gloss:validator|validator]] picked it), the [[gloss:evm|EVM]] starts executing instructions one by one, and at some point hits a failing require() condition or runs out of [[gloss:gas|gas]]. The [[gloss:evm|EVM]] rolls back every state change the [[gloss:transaction|TX]] made and marks it as "reverted". Funds didn't [[gloss:move|move]], the [[gloss:nft|NFT]] wasn't minted, the swap didn't happen. But the [[gloss:gas|gas]] spent up to the point of failure is taken from your balance. This is by design: without paying for failed TXs, anyone could attack the network with an endless stream of [[gloss:transaction|transactions]] that fail on the first instruction – zero cost to the attacker, huge load on [[gloss:validator|validators]]. The [[gloss:gas|gas]] for a revert is the fee for the [[gloss:validator|validator]]'s CPU that did run your code to the point of failure. Typical causes: out-of-[[gloss:gas|gas]] (you set gasLimit below what was needed), slippage too high (the market moved), require failed in the contract (for example, you don't have the right allowance for transferFrom), the [[gloss:nonce|nonce]] was already used. :::

:::p{html} When a [[gloss:transaction|transaction]] has been sent but doesn't appear in a new [[gloss:block|block]] within 5–10 minutes, there are three possible causes: :::

:::p{html}

  • maxFeePerGas below the current baseFee. The network jumped above your ceiling, the [[gloss:validator|validator]] will skip the [[gloss:transaction|TX]] until the price drops. See the mechanics in §2.
  • [[gloss:nonce|Nonce]]-gap. You have an earlier [[gloss:transaction|TX]] with a smaller [[gloss:nonce|nonce]] that hasn't landed yet. In [[gloss:ethereum|Ethereum]], [[gloss:nonce|nonce]] is sequential: [[gloss:transaction|TX]] #11 can't be included until #10 is.
  • Network congestion. Rare after [[gloss:fusaka|Fusaka]], but still happens during events like a hot [[gloss:nft|NFT]] mint or a mass liquidation. Just a queue.
:::

:::p{html} In Etherscan such a [[gloss:transaction|TX]] shows up as "Pending" in the top panel. From the user's side it looks like "nothing is happening", but on-chain exactly one thing is happening: your signature is sitting in the [[gloss:mempool|mempool]], waiting. :::

:::p{html} [[gloss:wallet|Wallets]] usually show two buttons on a stuck [[gloss:transaction|TX]]: Speed up and Cancel. Under the hood, both do the same thing: send a new [[gloss:transaction|transaction]] with the same [[gloss:nonce|nonce]] but a higher [[gloss:gas|gas]] price. The [[gloss:validator|validator]] includes whichever is more profitable, the other becomes invalid. The difference is only in the payload: :::

:::p{html}

  • Speed up – the same [[gloss:transaction|TX]] as the original, but more expensive. If it lands, you get the expected result, paying more [[gloss:gas|gas]].
  • Cancel – an empty [[gloss:transaction|TX]] (transfer 0 [[gloss:ethereum|ETH]] to your own address) with the same [[gloss:nonce|nonce]] and a higher [[gloss:gas|gas]] price. If it lands, the original doesn't go through, but you still pay for the empty transfer (a minimum of 21,000 [[gloss:gas|gas]]).
:::

:::p{html} An important detail: cancel isn't guaranteed. If the original [[gloss:transaction|TX]] has already propagated through the network and the [[gloss:validator|validator]] picked it before your cancel-[[gloss:transaction|TX]] reached the builder, the original lands and the cancel becomes a wasted payment. The probability is especially high if the [[gloss:transaction|TX]] is interesting to a [[gloss:mev|MEV]] bot (a profitable swap, for example): the bot pulls it into its bundle through a private channel, and your cancel coming via the public [[gloss:mempool|mempool]] never catches up. :::

:::p{html} RPC is the address of the [[gloss:node|node]] your [[gloss:wallet|wallet]] talks to the [[gloss:blockchain|blockchain]] through (see 1.2 §6 for the introduction). You sign a [[gloss:transaction|transaction]] → the [[gloss:wallet|wallet]] sends it to the RPC → the RPC broadcasts it to the network. You query a balance → the [[gloss:wallet|wallet]] asks the RPC. Every [[gloss:wallet|wallet]] has a default RPC: :::

:::p{html}

  • MetaMask defaults to Infura. Infura belongs to Consensys, the same company that owns MetaMask. So Consensys sees every request you make.
  • Rabby uses public RPCs for each network; the user can configure their own.
  • Trust [[gloss:wallet|Wallet]] uses its own set of [[gloss:node|nodes]].
:::

:::p{html} On October 20, 2025, when AWS US-EAST-1 went down, Infura went down with it, and MetaMask was effectively offline for tens of minutes. In parallel, every major [[gloss:layer 2|L2]] went dark: Arbitrum, Optimism, Base, Polygon, Linea, Scroll – because their public RPCs are also hosted on AWS. [[gloss:ethereum|Ethereum]] kept producing [[gloss:block|blocks]], but 99% of users couldn't talk to it. That's a concrete case of centralization: "decentralized" infrastructure on a single cloud platform. :::

:::p{html} Why switch the RPC: :::

:::p{html}

  • Privacy. Your current RPC sees your IP, which dApps you use, your [[gloss:wallet|wallet]] balance, the frequency of your requests. That's commercial data.
  • Speed. If your RPC lags (rate-limited, overloaded), TXs go out in seconds instead of instantly.
  • [[gloss:mev|MEV]] protection. [[gloss:flashbots|Flashbots]] Protect and [[gloss:mev|MEV]] Blocker are RPCs that drop TXs into a private pool – see §3.
  • Availability. If the default RPC goes down, having an alternative pre-configured in settings is insurance against an October 20, 2025 type event.
:::

:::p{html} A concrete list of RPCs by use case is in Advanced 1. :::

:::p{html} A handful of situations everyone runs into at least once: :::

:::p{html}

  • Wrong chain / [[gloss:chainid|chainId]] mismatch. You're on Arbitrum, the dApp expects [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] (or vice versa). The signature is valid only for a specific network ([[gloss:chainid|chainId]] is baked into the signature, see §1), so the [[gloss:transaction|TX]] just doesn't go through. The UI usually shows "Switch network".
  • Insufficient funds for [[gloss:gas|gas]]. Your address has USDC, but no [[gloss:ethereum|ETH]] (or not enough of the native token for [[gloss:gas|gas]]). Fix: top up the native token.
  • [[gloss:nonce|Nonce]] too low. The [[gloss:wallet|wallet]] tried to send a [[gloss:transaction|TX]] with a [[gloss:nonce|nonce]] that's already been used (for example, after switching RPC the [[gloss:wallet|wallet]] didn't refresh the counter). Fix: reconnect the [[gloss:wallet|wallet]], or manually edit the [[gloss:nonce|nonce]].
  • Slippage too high. The price moved more than you allowed. [[gloss:transaction|TX]] reverts. See §3 on slippage as [[gloss:mev|MEV]] defense.
:::

:::callout{icon=💡} A car that "won't do something". Won't start, won't drive, dashboard flashing. A mechanic plugs in a diagnostic scanner and reads the codes: P0301 (cylinder 1 misfire), P0420 (catalytic converter efficiency), and so on. Each code is a specific failure, and each one maps to a specific fix. A revert is a code on the dashboard: the car drove a little and stopped, fuel went down a bit, but you didn't reach your destination. A stuck [[gloss:transaction|TX]] is a car idling: everything's on, the engine is running, but there's no [[gloss:movement|movement]] – maybe not enough power (low [[gloss:gas|gas]]) or there's a queue ahead. Speed-up is "press the accelerator harder", cancel is "turn around and go home" (instead of the planned trip you got an empty mileage). Switching RPC is switching the auto shop: same cars, same parts, but a different crew, different queues, a different price for the diagnostic. Every error code in the [[gloss:evm|EVM]], like every OBD code, has an unambiguous meaning, and learning to use [[gloss:defi|DeFi]] is largely getting familiar with the table. :::

:::keypoints{label="Key Points"}

  • Many people read the [[gloss:gas|gas]] on a failed [[gloss:transaction|TX]] as a penalty, but it's the same standard charge: (baseFee + priorityFee) × gasUsed. The only difference is the value of gasUsed: for a success it's the full execution cost, for a revert it's whatever the [[gloss:evm|EVM]] actually consumed before stopping. baseFee is burned in both cases, priorityFee goes to the [[gloss:validator|validator]] in both cases. The network doesn't distinguish "success" from "revert" economically – only the user does.
  • If you sent five TXs in a row ([[gloss:nonce|nonce]] 10, 11, 12, 13, 14) and [[gloss:transaction|TX]] #11 got stuck on low [[gloss:gas|gas]], then #12-14 won't go through either, even if their [[gloss:gas|gas]] is higher. That's the specifics of sequential [[gloss:nonce|nonce]]: the [[gloss:blockchain|blockchain]] won't process #12 until #11 lands. [[gloss:wallet|Wallets]] often don't surface this dependency clearly, three TXs just sit pending without explanation. The fix is always the same: speed-up or cancel the earliest stuck [[gloss:transaction|TX]], the rest follow automatically.
  • There's no real "cancel" command in the [[gloss:ethereum|Ethereum]] protocol. The [[gloss:wallet|wallet]] button labeled Cancel is a UX shortcut for "place a higher bid on the original's [[gloss:nonce|nonce]] position": you submit an empty [[gloss:transaction|TX]] reusing that [[gloss:nonce|nonce]] and offering a richer tip, hoping the [[gloss:validator|validator]] finds it more profitable to include than the one you're trying to drop. The mental model isn't "undo", it's auction escalation. Every time you click Cancel, you're paying for a chance, not buying a refund.
  • Most people treat the RPC as a technical detail; it's actually who sees your activity. Your provider observes everything that passes through: addresses, balances, query timing, dApp choices. MetaMask defaults to Infura (owned by the same company as the [[gloss:wallet|wallet]]); Rabby uses public provider [[gloss:node|nodes]]. Picking a different RPC means picking a different party that knows what you do on-chain. The AWS outage on October 20, 2025 added a second angle: one RPC is also one point of failure. :::

:::details{badge=Practice title="A catalog of RPCs by use case"} :::p{html} You can see your current RPC in your [[gloss:wallet|wallet]]: MetaMask Settings → Networks → [[gloss:ethereum|Ethereum]] [[gloss:mainnet|Mainnet]] → RPC URL. To add a new one, go to Networks → Add a network. A concrete list of addresses as of May 2026: :::

:::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} Good practice is to add two RPCs and switch manually: one main ([[gloss:flashbots|Flashbots]] for most swaps, say), one backup (llamarpc for read-only operations). If one lags, the other saves you. :::

:::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]].
::: :::

:::details{badge=Advanced title="The evolution of transaction types and their reverts"} :::p{html} Every major protocol upgrade added a new [[gloss:transaction|transaction]] type, and each has its own revert scenarios. Knowing this is useful because the error messages in [[gloss:wallet|wallets]] directly reflect the format. :::

:::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} When you see an error message with an opaque code in Etherscan, the first thing to do is look at the Type field of the [[gloss:transaction|TX]] and understand which reverts are typical for that type. As of January 2026, about 80% of reverts on [[gloss:mainnet|mainnet]] are types 2 (1559) and 3 ([[gloss:blob|blob]]); on [[gloss:layer 2|L2]], type 3 dominates. :::

:::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.
::: :::

:::quiz{id=q-3-3-s6 correct=1 explanation="Correct. This is a classic [[gloss:nonce|nonce]]-gap. In [[gloss:ethereum|Ethereum]] [[gloss:nonce|nonce]] is sequential: [[gloss:transaction|TX]] #6 can't be included until #5 is, regardless of how much higher #6's [[gloss:gas|gas]] is. The high [[gloss:gas|gas]] on #6 and #7 works against you here: you paid for priority, but the priority is unreachable while the [[gloss:nonce|nonce]] is blocked. The fix: speed up specifically [[gloss:nonce|nonce]] 5 (raise its [[gloss:gas|gas]]), and all three will land in order. A doesn't work: if congestion were the issue, a general speed-up would help, but the problem here is local. C doesn't work: a reverted [[gloss:transaction|TX]] doesn't sit pending, it lands in a [[gloss:block|block]] with a failed status. D – [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] has never had a full halt since 2015."} You sent three [[gloss:transaction|transactions]] to Uniswap in a row: swap 100 USDC → [[gloss:ethereum|ETH]] ([[gloss:nonce|nonce]] 5, low [[gloss:gas|gas]]), swap 200 DAI → [[gloss:ethereum|ETH]] ([[gloss:nonce|nonce]] 6, medium [[gloss:gas|gas]]), add liquidity ([[gloss:nonce|nonce]] 7, high [[gloss:gas|gas]]). Ten minutes later, all three are sitting at "pending" in Etherscan. What happened and what do you do?

  • The [[gloss:ethereum|Ethereum]] network is congested, just wait
  • [[gloss:transaction|TX]] [[gloss:nonce|nonce]] 5 is stuck on low [[gloss:gas|gas]], TXs 6-7 are waiting for it. Speed up specifically [[gloss:nonce|nonce]] 5
  • High slippage tolerance reverted all three
  • [[gloss:ethereum|Ethereum]] is down, check the status page :::

:::section{mins=8} L2: Same Rules, Different Physics :::

:::lead On Arbitrum or Base, a user makes a swap for $0.001. On [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]], the same swap costs $0.13 – a hundred times more. The rules are the same; [[gloss:layer 2|L2s]] just split the work differently. Execution happens cheaply on a separate chain, while [[gloss:l1|L1]] is used only for the final publication of compressed data. Same application, same [[gloss:evm|EVM]], same user signature, but the price is a hundred times lower, and under the hood several things work differently. :::

:::p{html} A [[gloss:transaction|transaction]] on [[gloss:layer 2|L2]] pays twice. Execution [[gloss:gas|gas]] is the fee for computation inside the [[gloss:layer 2|L2]] network: calculated by the same formula (baseFee + priorityFee) × gasUsed as on [[gloss:l1|L1]] (see §2), but with baseFee in hundred-thousandths of a Gwei. [[gloss:l1|L1]] data cost is the fee for publishing compressed data of your [[gloss:transaction|transaction]] to [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]], because ultimately [[gloss:l1|L1]] stores [[gloss:layer 2|L2]]'s history and provides its security. That second part is the main contribution to a swap price on [[gloss:layer 2|L2]]: in a typical [[gloss:transaction|transaction]], 80–90% of the cost goes to [[gloss:l1|L1]] publication, not to the actual computation. Before March 2024, [[gloss:l1|L1]] data cost dominated, and a swap on Arbitrum cost 10–20 cents. After [[gloss:eip-4844|EIP-4844]] ([[gloss:dencun|Dencun]]) activated, [[gloss:blob|blob]] [[gloss:transaction|transactions]] appeared: data is stored for a limited time (about 18 days) and costs orders of magnitude less than regular calldata. After [[gloss:dencun|Dencun]], a typical swap on Base costs $0.001 instead of $0.10–$0.20. :::

:::p{html} On [[gloss:ethereum|Ethereum]] [[gloss:l1|L1]] the [[gloss:mempool|mempool]] is public (see §3): anyone can see pending TXs, any bot can read your [[gloss:transaction|transaction]] before it lands in a [[gloss:block|block]]. [[gloss:layer 2|L2]] architecture is different. Every [[gloss:layer 2|L2]] has a central [[gloss:sequencer|sequencer]] – a single point that accepts incoming [[gloss:transaction|transactions]], orders them, and publishes a batch to [[gloss:l1|L1]]. On Arbitrum, the [[gloss:sequencer|sequencer]] is operated by Offchain Labs; on Optimism, by OP Labs; on Base, by Coinbase. Several things follow: there is no public [[gloss:mempool|mempool]] in the usual sense, sandwich attacks through bot front-running are dramatically reduced, but you get a single point of failure. On October 20, 2025, when AWS US-EAST-1 went down, Arbitrum, Optimism, Base, Polygon, Linea, and Scroll were unavailable simultaneously: their sequencers all host on AWS. Force inclusion (a backup mechanism for sending a [[gloss:transaction|TX]] directly through [[gloss:l1|L1]], bypassing the [[gloss:sequencer|sequencer]]) technically works, but takes about 24 hours and is meant for genuine censorship cases, not regular swaps. :::

:::p{html} Since [[gloss:layer 2|L2]] has no public [[gloss:mempool|mempool]], the classic sandwich attack via [[gloss:mempool|mempool]] bots (see §3) is technically impossible. That's why on Arbitrum or Base, users in 2026 effectively don't run into sandwich losses. But [[gloss:mev|MEV]] doesn't disappear: its source shifts. The [[gloss:sequencer|sequencer]] itself can in theory extract [[gloss:mev|MEV]] by ordering [[gloss:transaction|transactions]] inside its [[gloss:block|block]] – [[gloss:l2beat|L2BEAT]] documents this as a known risk for any centralized [[gloss:sequencer|sequencer]]. On Base, the [[gloss:sequencer|sequencer]] orders [[gloss:transaction|transactions]] by descending priorityFee, so searchers compete through tips and part of the extracted value flows back to Coinbase as [[gloss:sequencer|sequencer]] revenue (disclosed in their 10-K). Arbitrum uses timestamp-based ordering, which is harder to game but still doesn't fully eliminate [[gloss:mev|MEV]]. In practice no large-scale operator-driven [[gloss:mev|MEV]] extraction has been documented on either chain in 2024–2026; the deterrent is reputational rather than cryptographic. For a user doing a large swap on [[gloss:layer 2|L2]], the threat at the same scale as on [[gloss:l1|L1]] (see the March 2025 case in §3) doesn't exist, but the trust assumption is different: you're trusting the operator's incentive not to get caught, not a cryptographic guarantee. :::

:::p{html} On [[gloss:l1|L1]], a [[gloss:transaction|transaction]] lands in a [[gloss:block|block]] in 12 seconds and is considered finalized after about 13 minutes (two epochs). On [[gloss:layer 2|L2]], the [[gloss:sequencer|sequencer]] forms a "soft [[gloss:block|block]]" in 250 ms to 2 seconds: for the user the [[gloss:transaction|transaction]] is effectively instant. But that's a soft confirmation from the [[gloss:sequencer|sequencer]], not the final security. Full [[gloss:finality|finality]] happens when [[gloss:layer 2|L2]] publishes the batch to [[gloss:l1|L1]] and the challenge window passes: on optimistic [[gloss:rollup|rollups]] (Arbitrum, Optimism, Base) that's 7 days; on ZK [[gloss:rollup|rollups]] (zkSync, Linea, Scroll, Starknet) it's from an hour to a few hours after the [[gloss:validity proof|validity proof]] posts. For regular activity inside [[gloss:layer 2|L2]] the difference doesn't matter: you swap, you mint, you put funds in a pool – all of that happens instantly from the [[gloss:layer 2|L2]]'s perspective. The challenge window only matters when you withdraw from [[gloss:layer 2|L2]] back to [[gloss:l1|L1]]. For fast withdrawals there are bridges like Across, Hop, LayerZero, which give you funds on [[gloss:l1|L1]] immediately and take a fee for absorbing the wait themselves. :::

:::p{html} Most [[gloss:defi|DeFi]] practices [[gloss:move|move]] from [[gloss:l1|L1]] to [[gloss:layer 2|L2]] unchanged, but a few things need to stay in mind: :::

:::p{html}

  • Every [[gloss:layer 2|L2]] is a separate network with its own [[gloss:chainid|chainId]]. A signature for Arbitrum ([[gloss:chainid|chainId]] 42161) doesn't work on Optimism ([[gloss:chainid|chainId]] 10) or Base ([[gloss:chainid|chainId]] 8453). If the [[gloss:wallet|wallet]] isn't on the right network, the [[gloss:transaction|TX]] simply doesn't go through.
  • [[gloss:nonce|Nonce]] is independent on each network. Your address is the same (for an [[gloss:eoa|EOA]]), but the [[gloss:nonce|nonce]] counter runs separately on each [[gloss:layer 2|L2]]. It doesn't interact with [[gloss:l1|L1]].
  • Allowance and Permit2 permissions don't carry across. An allowance is a record in the token contract's storage. USDC on [[gloss:mainnet|mainnet]] and USDC on Arbitrum are two different contracts with separate storage. The approve you made a year ago on [[gloss:mainnet|mainnet]] doesn't exist on Arbitrum. Permit2 has the same address 0x000…22D473 on all [[gloss:evm|EVM]] chains (it was deployed via CREATE2), but it too needs a separate allowance on each network.
  • Revoke.cash works separately on each network. If you run cleanup, switch networks in the site's top menu (see §5).
  • RPCs for [[gloss:layer 2|L2]] differ from [[gloss:l1|L1]]. Arbitrum is arb1.arbitrum.io/rpc, Base is [[gloss:mainnet|mainnet]].base.org, Optimism is [[gloss:mainnet|mainnet]].optimism.io (see §6 Advanced 1).
:::

:::callout{icon=💡} One international hub airport and a dozen regional ones. Planes fly by the same ICAO rules, crews go through the same certifications, cargo moves under the same customs standards. But a ticket through the big hub on long routes is expensive, and a flight between two small cities on a regional carrier is pennies. The regional airports are cheap because they don't do everything themselves: their flights connect through the big hub for onward routing. If something goes wrong at the big hub, the regional ones suffer too: their own flights still take off, but the passengers can't enter the international network. The same way [[gloss:layer 2|L2s]] execute their own [[gloss:transaction|transactions]] cheaply and quickly, but the final "legalization" of those [[gloss:transaction|transactions]] happens through [[gloss:l1|L1]]. The AWS outage of October 20, 2025 added another side to the analogy: if several regional airports share a single dispatch center, that center failing grounds them all at once, even when the runways themselves are fine. :::

:::keypoints{label="Key Points"}

  • When you see a $0.001 price tag on a Base swap, about 80–90% of that goes to [[gloss:l1|L1]]: to publish a [[gloss:blob|blob]] [[gloss:transaction|transaction]] with your swap's data. The computation inside Base itself is pennies. That explains an oddity: a simple transfer on [[gloss:layer 2|L2]] costs almost as much as a complex swap, because the published bytes are comparable in size between the two. And that's why when [[gloss:l1|L1]] baseFee spikes (rare, but it happens), prices on every [[gloss:layer 2|L2]] rise in sync.
  • On [[gloss:l1|L1]], [[gloss:validator|validators]] are an open set of thousands of independent participants. On [[gloss:layer 2|L2]], the [[gloss:sequencer|sequencer]] is a specific legal entity: Offchain Labs for Arbitrum, OP Labs for Optimism, Coinbase for Base. That brings speed and predictability but takes away the cryptographic guarantee: if the [[gloss:sequencer|sequencer]] censors you, force inclusion through [[gloss:l1|L1]] with a 24-hour delay is the fallback. In 2026 major operators behave honestly, but the discipline is reputational, not cryptographic – a different category of defense than trustless [[gloss:l1|L1]].
  • For an [[gloss:eoa|EOA]] the address is identical across all [[gloss:evm|EVM]] chains, but token contracts keep separate storage. An approve(Permit2) on [[gloss:mainnet|mainnet]] USDC writes into [[gloss:mainnet|mainnet]] USDC's storage; there's no matching entry in Arbitrum USDC, you'll need a fresh approve. The same applies to [[gloss:nonce|nonce]], balance, and allowance toward any dApp. Counter-intuitive for anyone thinking "I have the same [[gloss:wallet|wallet]]". For cleanup through revoke.cash, repeat the process on each network separately.
  • Inside [[gloss:layer 2|L2]] your [[gloss:transaction|transaction]] is included in seconds and immediately executes: swap, deposit, mint happen instantly. The 7-day challenge window (on optimistic [[gloss:rollup|rollups]]) or the hour-long one (on ZK [[gloss:rollup|rollups]]) is about when withdrawal from [[gloss:layer 2|L2]] to [[gloss:l1|L1]] becomes cryptographically safe. For day-to-day work inside [[gloss:layer 2|L2]] it doesn't matter. For large withdrawals it's a safety net: either wait the window, or use a paid fast bridge that absorbs the risk for you. :::

:::details{badge=Advanced title="Force inclusion and censorship resistance"} :::p{html} The body of this section establishes that force inclusion exists as an escape hatch; the architectural reasoning sits in 2.5 §3. Here we cover the concrete how – which contracts on [[gloss:l1|L1]] to call and what delay to expect on each [[gloss:rollup|rollup]]. :::

:::p{html} Arbitrum One: the Inbox contract on [[gloss:l1|L1]] exposes sendL2Message(bytes calldata messageData). Your encoded [[gloss:layer 2|L2]] [[gloss:transaction|transaction]] goes into the delayed inbox, and the [[gloss:sequencer|sequencer]] must order it within roughly 24 hours. If the [[gloss:sequencer|sequencer]] doesn't, anyone can call forceInclusion() on the bridge contract to insert it directly. :::

:::p{html} Optimism, Base, and other [[gloss:op stack|OP Stack]] chains: deposits go through the OptimismPortal contract's depositTransaction method (the underlying messenger is L1CrossDomainMessenger). Under normal conditions deposits appear on [[gloss:layer 2|L2]] within about 30 minutes. If the [[gloss:sequencer|sequencer]] goes offline, the protocol guarantees inclusion no later than the end of the 12-hour sequencing window – much faster than Arbitrum's 24-hour delay, at the cost of paying [[gloss:l1|L1]] [[gloss:gas|gas]] at submission time. :::

:::p{html} ZK [[gloss:rollup|rollups]] (Scroll, Linea, zkSync, Starknet): each has its own [[gloss:l1|L1]] entry contract with similar semantics. Delays vary from minutes to hours depending on the chain's proof posting cadence. :::

:::p{html} In all cases the [[gloss:l1|L1]] [[gloss:gas|gas]] cost makes force inclusion impractical for a $50 swap, but realistic for a withdrawal of meaningful size when the [[gloss:sequencer|sequencer]] is unresponsive – exactly the failure mode the October 20, 2025 AWS outage briefly demonstrated. ::: :::

:::quiz{id=q-3-3-s7 correct=2 explanation="Correct. Your [[gloss:wallet|wallet]]'s address is the same across every [[gloss:evm|EVM]] network, but that doesn't mean the storage of the contracts is the same. USDC on [[gloss:mainnet|mainnet]] and USDC on Arbitrum are two different [[gloss:smart contract|smart contracts]] on two different networks, each with its own storage. The approve you ran a year ago on [[gloss:mainnet|mainnet]] wrote "permission for Permit2 to spend your USDC" into [[gloss:mainnet|mainnet]] USDC's storage. On Arbitrum USDC there's no such entry. That's why a new approve is needed. B is not quite right: Permit2 has the same address 0x000000000022D473030F116dDEE9F6B43aC78BA3 across all [[gloss:evm|EVM]] chains, because it was deployed via CREATE2 with the same init code, so it's the same intermediary contract. A doesn't work: MetaMask doesn't "forget" an approve, because the approve isn't stored in the [[gloss:wallet|wallet]] but in the contract. D is wrong: the same ERC-20 standard."} You ran a USDC → [[gloss:ethereum|ETH]] swap on Uniswap V3 on [[gloss:ethereum|Ethereum]] [[gloss:mainnet|mainnet]] in February 2024, and MetaMask set approve(Permit2, unlimited) on your USDC. Today you try the same Uniswap V3 on Arbitrum One, with the same [[gloss:wallet|wallet]] and the same address. On the first swap MetaMask asks you to sign approve again. Why?

  • MetaMask forgot the previous approve, you need to reconnect the [[gloss:wallet|wallet]]
  • Permit2 on Arbitrum is a different contract from the [[gloss:mainnet|mainnet]] one
  • Every [[gloss:layer 2|L2]] is a separate network with its own storage; allowances on [[gloss:mainnet|mainnet]] USDC and Arbitrum USDC are stored separately
  • Uniswap V3 on [[gloss:layer 2|L2]] uses a different token standard, not ERC-20 :::

:::section{mins=7} Before You Sign :::

:::lead Every signature in Web3 happens in the gap between "press Confirm" and "don't press Confirm" – a few seconds that contain everything you've worked through in this card. This final section is the algorithm for those seconds: what to see on the [[gloss:wallet|wallet]] screen, what to verify in the browser tab, which red flags cancel the operation automatically. Not theory, but a ritual. :::

:::p{html} When the [[gloss:wallet|wallet]] pops up with a request, you have a few seconds to scan it. The minimum that should be visible before your finger moves toward Confirm: :::

:::p{html}

  • The request type at the top: eth_sendTransaction, personal_sign, eth_signTypedData, or (red flag) eth_sign – see §4.
  • For a [[gloss:transaction|transaction]]: recipient (to), amount (value), decoded data if the [[gloss:wallet|wallet]] shows it. If data is bare hex, that's already a slow-down signal.
  • For [[gloss:eip-712|EIP-712]]: the dApp's name in domain.name, the contract address in domain.verifyingContract, the network ID in domain.[[gloss:chainid|chainId]], the decoded message.
  • The simulation result, if the [[gloss:wallet|wallet]] shows one (Rabby, MetaMask with Blockaid). "You will receive X, you will send Y" – if the simulation shows something strange or shows nothing, that's a cancel signal.
  • Origin: which site sent the request. It must match what's in your browser's address bar. A mismatch is a textbook clipboard-malware attack.
:::

:::p{html} The [[gloss:wallet|wallet]] screen only shows what the dApp sent. Before signing, you need to confirm the dApp itself is who it claims to be. :::

:::p{html}

  • The URL in the address bar – read it twice, character by character. uniswap.org and unisw4p.org look identical in a screenshot but are different domains. Substitutions via Unicode lookalikes or letter pairs (l → 1, o → 0) are standard drainer-site practice.
  • HTTPS and certificate: the lock should be there. If the browser complains about the certificate, cancel and check.
  • How you got to this site: if you opened it from your own bookmark, that's a safe path. If you clicked a link from a DM, an email, a tweet, or a Telegram message, the risk level is higher – verify everything three times.
  • The origin in the [[gloss:wallet|wallet]] request must match the URL in the tab. When they don't, it's either clipboard malware or an embedded iframe from a phishing site on top of a legitimate one. Either way, cancel.
:::

:::p{html} This is a list of situations where you don't think further, you press Cancel and figure it out after. The checklist is built so the moment of decision has no ambiguity: :::

:::p{html}

  • An eth_sign request to sign an arbitrary [[gloss:hash|hash]]. See §4: the method is deprecated, no legitimate dApp uses it.
  • Unlimited allowance (a 78-digit number ending in ...9935) for a one-time operation. A [[gloss:permit|permit]]/approve for a specific amount is normal; unlimited is only normal for an active [[gloss:defi|DeFi]] pipeline, not for an airdrop claim.
  • A deadline far in the future: December 31, 2099 or similar. Any honest dApp sets deadline in minutes or hours.
  • A spender you don't recognize. Open it on Etherscan: if the contract was created yesterday and has three [[gloss:transaction|transactions]], it's not Uniswap.
  • A [[gloss:hash|hash]] instead of a decoded structure: the [[gloss:wallet|wallet]] shows 0x4a8c2d... and nothing else. That's blind signing – see §4.
  • The browser URL isn't what you expected, even if the difference is a single letter.
  • The [[gloss:wallet|wallet]] shows a warning via Blockaid or Rabby's built-in protection. These services know thousands of known drainer domains; trust them.
  • The amount is larger than you planned. Even by fractions of a percent, that's a signal to check again.
  • An [[gloss:eip-7702|EIP-7702]] request from a site you didn't open with that purpose. [[gloss:eip-7702|EIP-7702]] is for specific operations (smart [[gloss:wallet|wallet]], batching); if the site is doing a regular swap and asks for 7702, cancel.
:::

:::p{html} A checklist you read once and forgot doesn't protect you. What protects you is a habit – an automatic 3-to-5-second pause between the [[gloss:wallet|wallet]] popup and pressing Confirm. In those seconds the eyes have time to scan the screen and notice a mismatch before the brain rationalizes "looks fine". Drainer attacks in 2025-2026 are built precisely on the absence of that pause: a nice-looking airdrop site, the [[gloss:wallet|wallet]] shows a [[gloss:permit|permit]], the user presses Confirm because "I'm on the claim page, everything's in order". A hardware [[gloss:wallet|wallet]] with a large screen (Ledger Stax/Flex, Trezor Safe 5) physically slows the process down: confirmation requires pressing a separate button after reading the decoded content. That's the built-in pause. Most people who lose funds to drainer attacks are not technically naive – they're in a hurry. If you're in a hurry, don't sign: an hour lost is cheaper than a signature. :::

:::callout{icon=💡} A commercial pilot's pre-flight checklist. Before every flight, regardless of how many years the pilot has at the yoke, they open a paper or electronic list and walk through the items: fuel, flaps, temperature, pressure, radio, light from dispatch. Reading takes 2-3 minutes. An experienced pilot can do it on autopilot, but they do it, because there are documented cases where skipping the checklist cost lives. The same with a Web3 signature: every time before Confirm – the same short scan of the screen. An experienced [[gloss:defi|DeFi]] user can do it in 5 seconds, but they do it. When after a hundred signatures the user starts "just clicking" – that's the equivalent of a pilot skipping the checklist "because I know the plane by now". At that point a disaster becomes a matter of time. :::

:::keypoints{label="Key Points"}

  • After Confirm, you're no longer in the loop. The contract executes using your signature as authority, and that authority can't be "undone later" – it stays valid, as we saw in §4, until explicitly revoked. So all of the checking has to happen before Confirm. The psychology is counter-intuitive: users read Confirm as "complete a purchase", but it's "issue permission to spend". A purchase is one-shot; a permission keeps working.
  • On a Ledger Stax or Trezor Safe 5, [[gloss:eip-712|EIP-712]] is decoded by the device itself, without trusting the browser. An extension [[gloss:wallet|wallet]] (MetaMask, Rabby) runs inside the same browser as the dApp and sees the same data (see §4). If the dApp is tampered with, the extension shows a "correct" signature while the actual content underneath differs. A separate device is the only place where the decoded structure is independent. The amount doesn't matter – $50 or $50,000, the attack mechanism is identical.
  • Crypto Twitter is full of stories about engineers, analysts, and smart-contract developers losing tens of thousands through a "claim airdrop" click. These people have no problem understanding how signatures work; they have a problem with the pause before Confirm. The defense here isn't knowledge, it's pace. Someone who always spends 5 seconds before signing is better protected than someone who's read Uniswap's documentation but signs fast.
  • The most common mistake is pressing Confirm "because I'm already here" – sunk-cost thinking. In reality, cancellation costs nothing (the [[gloss:transaction|TX]] isn't sent, no [[gloss:gas|gas]] is spent), and the possible loss can be enormous. When doubt arises, cancellation is always the right action. You can come back in an hour, verify, and sign again. You can also not come back at all. No dApp suffers from you changing your mind at the last second. :::

:::details{badge=Practice title="Minimum security setup for 2026"} :::p{html} The concrete kit that drops most drainer risk to near zero: :::

:::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} Total setup cost: $250-400 in hardware plus an hour to set things up. That's the minimum threshold for safe [[gloss:defi|DeFi]]. :::

:::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.
::: :::

:::details{badge=Advanced title="If you already signed something bad: the first 30 minutes"} :::p{html} Drainer attacks usually don't drain immediately. Between the signature and the drain there's typically minutes to hours, sometimes days – the attacker waits for you to sign more or for prices to [[gloss:move|move]]. You have a window to react. :::

:::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} Exit cost: $10-30 in [[gloss:gas|gas]] for all revokes and transfers on [[gloss:mainnet|mainnet]], pennies on [[gloss:layer 2|L2]]. Compared to a potential loss, that's nothing. The key is to act in the first minutes, not hours. :::

:::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. Старый кошелёк нельзя «вылечить», его сид-фраза уже у атакующего. Перенесите всё, оставьте пустой.
::: :::

:::quiz{id=q-3-3-s8 correct=2 explanation="Correct. Even when the URL is correct and the contract is genuine, unlimited + deadline 2099 are red flags by themselves. It's equivalent to a permanent permission to spend any amount of USDC, valid effectively until the end of the century. No honest dApp needs that for a single swap – in a normal flow, Uniswap V3 requests [[gloss:permit|permit]] for a specific amount with a deadline in hours. If the interface asks for unlimited + far deadline, it's either tampering through compromised JS or a default setting that you can (and should) override – most [[gloss:wallet|wallets]] let you edit the value before signing. A and B are wrong: the correctness of one or two fields doesn't justify ignoring two red flags in other fields. D is wrong: V3 and V4 coexist, V3 isn't "deprecated"."} You open MetaMask on a laptop and see a signature request. The [[gloss:wallet|wallet]] shows: type eth_signTypedData, domain.name: "Uniswap V3", verifyingContract: 0xE592...64 (a known Uniswap router address), [[gloss:chainid|chainId]]: 1, message: [[gloss:permit|permit]] USDC, value: unlimited, spender: 0xE592...64, deadline: December 31, 2099. URL in the browser tab: app.uniswap.org. What do you do?

  • Sign – it's Uniswap, a known contract
  • Sign – domain.name and URL match, the contract is right, everything's in order
  • Cancel – unlimited + deadline 2099 are red flags by themselves, even with a correct URL and contract
  • Cancel – Uniswap V3 is deprecated, switch to V4 :::