
A wallet drain is usually not a broken curve. The key stays where it should, and the funds leave because the owner signed a request that gave someone else permission to move them. The pattern repeats: a page that looks like a mint, a connect dialog, a signature that costs no gas, and a balance that disappears later.
The common path
The sequence has four steps and only one of them involves you.
A link arrives promising an airdrop, a whitelist place, or a free mint. The page connects to your wallet and asks for a signature. The request is a permit, an approval or an operator approval, and it costs nothing to sign. Weeks or months later a transaction from an address you have never seen moves the assets.
Nothing was hacked in the sense most people mean. No key was extracted, no firmware was exploited, no exchange was breached. A permission was granted, and permissions do not expire.
Why the curve is not the target
secp256k1 has not been broken, and attacking it costs more than asking you to approve something. The economics point at the consent layer, so the attacks sit there.
So the defence is a procedure, not a chip: read the request, simulate it, refuse what you cannot understand.
Simulate, but do not worship the simulation
Transaction simulation runs the call against current chain state and shows the balance changes it expects. It is the best single pre-signature check available, and it has three limits.
First, it models the same bytes you are about to sign. If the payload is hostile, the simulation shows the hostile outcome honestly, which is useful only if you read it.
Second, state can change between simulation and execution. A contract can behave differently depending on a balance, a block number or a variable an attacker controls.
Third, proxies and delegate calls can make the simulated effect differ from the label. A simulation that says transfer is not a guarantee about which contract executed.
Treat a clean simulation as a filter, not a certificate.
Read the request, not the page
Four fields decide almost everything, and all four sit in the wallet popup rather than the website.
What function is being called. A transfer moves a specific amount. An approval grants a permission. A permit grants a permission with no gas, which is the one people sign without noticing.
Who receives the permission. For an approval this is the spender, and it matters most. A spender you cannot identify is a no.
How much. Unlimited is a choice the interface makes for you, and it turns one mistake into a full balance.
What it costs now. A zero fee request is not free. Its cost arrives later, when someone else uses the permission.
Keep savings behind a different key
The strongest structural control is separation. A wallet that connects to sites should hold roughly what you would carry in a physical wallet. Savings belong behind a key that has never seen a connect dialog and never will.
That rule caps the damage of any signature mistake at the spending balance, and it makes the decision easier, because the answer no longer involves your savings.
Address poisoning is the companion risk. A lookalike matching the first and last characters turns a copy and paste into a transfer to a stranger. The habit that closes it is in the address poisoning note.
| Request | What it can move | Revocable later | Field to check |
|---|---|---|---|
| Token transfer | A stated amount | No | Full destination address |
| Finite approval | Up to the allowance | Yes | Spender and amount |
| Unlimited approval | The whole balance | Yes, by revoking | The word unlimited |
| Permit signature | The whole balance | No, once used | Why it needs no gas |
| Operator approval | Every token in a collection | Yes, by revoking | The operator address |
| Batch call | Several of the above | Depends on the calls | Each action in the batch |
Where account abstraction changes the answer
Smart accounts can add session keys, spending limits and allowlists, which shrinks the blast radius of a bad signature. They do not remove the need to read the request, and they add a surface: the session key can be the thing that drains the account.
That trade is set out in the limits of account abstraction. The short version is that a policy is only as good as the code enforcing it, and that code is another contract you are trusting.
What to do next
Simulate every request you do not recognise, and refuse the ones you cannot decode. Enable the setting that blocks blind signing on the device that holds savings.
Then separate the keys. Move savings behind a wallet that never connects to a site, and keep a small balance for experiments. The hot wallet sizing question covers how much that small balance should be.
Finally, size the mistake. Ethereum.org’s L2 directory put Base at $14.6B of value in October 2026, and all of it moves on signatures. The loss is set by the balance behind the key, not by the fee in front of it.
0 comments
No comments yet — ask a question and we will answer it here.