Skip to content
Socket

Catalog / module / burn

Burn sink

v1

Burn tokens, provably.

What it does

burn-sink v1: Burn tokens, provably

A sink for one ERC-20 that destroys whatever reaches it and keeps two honest counters:

| Counter | Meaning | |---|---| | `totalBurned` | Destroyed with the token's own `burn()`. Each burn is checked: total supply and the sink's balance must both fall by exactly the amount. | | `totalSentToDead` | Sent to `0x…dEaD` because a verified burn was not possible (no `burn()`, a `burn()` that reverts, or a fake one). Supply does **not** change; the tokens are only out of reach. |

When the sink is set up it checks the token. If `totalSupply()` cannot be read, a burn can never be verified, so the sink is fixed to dead-address mode (`deadOnly = true`), and both the UI and the presets say so.

Once one real burn has worked (`burnProven`), the sink only burns from then on. If the token later refuses to burn, the call reverts so it can be retried; the sink never quietly switches to the dead address. A burn that runs out of gas also reverts rather than falling back, so nobody can push a burnable token onto the dead address with a tight gas limit.

Config (`BurnSinkConfig`)

`token`: the token to burn. It must be a contract. **Locked forever.**

Use

- `burnFrom(amount)`: approve the sink first. It pulls `amount` (measured by balance delta) and destroys it in the same call. Closed while paused, halted or migrated. - `burnAll()`: destroys every token sitting in the sink, for example from a route, a splitter or a direct transfer. Anyone can call it, in every state. - `burnInfo()`: token, mode, both counters and what is waiting. `history(from, count)` lists every burn.

Roles

- **Owner:** `pause`/`unpause` (blocks `burnFrom` only) and `rescue` for *other* tokens sent by mistake. The burn token is locked against rescue. - **Anyone:** `burnFrom`, `burnAll`.

Fee lanes

None. Burns are never charged; any fee was paid where the tokens came from (for example a burn-mint-nft or a splitter). The module is not flagged `FLAG_CHARGES_INFLOW`.

Risks in plain English

- If the token has no working `burn()`, the sink sends to the dead address instead. That is shown separately and is not a supply reduction. - A token with transfer fees burns what actually arrived, not what you approved. - The sink trusts the token's `totalSupply()` and `balanceOf()` to tell the truth when it verifies a burn.

What if

- **Someone sends tokens straight to the sink?** Anyone calls `burnAll()` and they are burned, even while paused. - **The token cannot burn?** Tokens go to `0x…dEaD` and are counted under `totalSentToDead`, never as burned. - **The owner loses their key?** Nothing changes. Burning is permissionless and the sink holds nothing between calls. - **The token pauses burning after it has worked?** `burnAll` reverts until burning works again; tokens wait in the sink and nobody can take them out.

Implementation notes

`burnFrom` pulls with a plain `transferFrom`, measured by balance delta, and credits the amount with `_creditUncharged`, which is the base's documented path for uncharged entries. The tokens then leave custody in the same call: by `_consume` (verified burn) or by `_pushOut` to the dead address. The verified burn runs as a self-call (`burnHeldVerified`, callable only by the sink), so a fake burn is rolled back before the fallback.

Conformance: the sink has no withdraw-style exit. `_cfExit` runs `burnAll()` and asserts that nothing is stuck, with zero custody and zero balance.

Risks in plain English

  • If the token has no working burn(), tokens go to 0x…dEaD. That is not a burn: total supply does not change, the tokens are only out of reach. The sink counts these separately as sent to dead.
  • Once one real burn has worked, the sink only burns: if the token later refuses to burn, burns revert until it works again instead of quietly switching to the dead address.
  • The owner can never take the burn token out of the sink; rescue only works for other tokens sent by mistake.

What if…

What if someone sends tokens straight to the sink?
Anyone can call burnAll() at any time, even while paused, and they are burned. Nothing can ever be withdrawn.
What if the token cannot burn?
The sink sends the tokens to the dead address 0x…dEaD and counts them under totalSentToDead, so nobody can claim they were burned.
What if the owner loses their key?
Nothing changes. burnFrom and burnAll are permissionless and the sink holds nothing between calls.
Is there a fee?
No. Burning is never charged; fees, if any, were paid where the tokens came from.

Fees

No protocol fee on this module.

Evidence

Form check
Built from the source files
Implementation
—
Code hash
—

Settings you choose at deploy

  • Token to burnBurns use the token's own burn() and are verified against totalSupply. Tokens that cannot burn go to the dead address and are counted separately.Locked forever