Abstract
Denim addsaddress(this) as a second trigger of the existing InvalidReceiver(address receiver) error. Any call to transfer, transferFrom, their memo variants, mint, mintWithMemo, batchMint, or seizeWithMemo that names the token’s own address as the recipient reverts. A B20 token is a precompile with no holder key; a credit to that address is not recoverable by the sender. Holder self-sends (from == to) are unaffected, and seizeWithMemo from the token address remains allowed so issuers can recover balances already stuck there.
Motivation
Users occasionally paste the token contract address instead of a recipient address. For standard ERC-20 tokens the tokens are stranded but potentially recoverable via governance. For B20 tokens — precompiles with no holder key — the sender cannot recover the funds at all. Only the issuer can, throughseizeWithMemo. There is no valid use case for a B20 token to hold its own tokens, so the correct handling is an immediate revert rather than a silent lock.
InvalidReceiver(address receiver) already fires for address(0) (ERC-6093). Denim extends it to cover address(this) using the same check, at the same position in the revert order, with no new selector, event, or function.
What Changed
Revert condition added to InvalidReceiver
The shared receiver guard that previously rejected only address(0) now also rejects address(this):
Before (Cobalt)
After (Denim)
Affected functions and revert order
The check runs at the existing invalid-receiver position in each function’s revert sequence:from may equal address(this) in seizeWithMemo. The check applies only to the to argument.
Code examples
Transfer to the token address reverts:Revert on transfer to token address
Revert on mint or seize to token address
Self-send still succeeds
Seize from token address to treasury
Migration
1
Treat the token address as an invalid recipient
Update wallets, custodians, and indexers to reject
address(token) as a destination the same way you already reject address(0). This applies before Denim activates.2
Expect InvalidReceiver after Denim activation
Any transfer, mint, or seize to
address(token) that succeeded before Denim will revert InvalidReceiver(address(token)) after activation. Update integrations that send to the token address accordingly.3
Recover stuck balances with seizeWithMemo
If tokens were credited to the token address before Denim activation, recover them with:The caller must hold
Recovery call
SEIZE_ROLE. The token must be seizable under SEIZE_EXEMPT_POLICY.4
Verify unaffected paths are unchanged
Holder-to-holder self-transfers, approvals, burns, and sends to other B20 tokens are not affected. Do not change handling for those cases.
Alternatives Considered
Reject any B20-prefix address as recipient. This would also block transfers to other B20 token addresses. Rejected because a prefix check cannot distinguish a B20 precompile from a user-controlled account in the same address space (for example, a multisig). Denim compares againstaddress(this) only.
Call isB20Initialized(to) on each credit path. This would reject only live tokens. Rejected because it adds a factory call on every credit path and does not address the paste-error use case precisely.
Introduce a new error such as SelfSend(address). A dedicated error would make traces clearer. Rejected because it adds ABI surface for a condition already covered by InvalidReceiver — “this destination is invalid.”
Also reject from == address(this) in seizeWithMemo. This would close the only recovery path for balances already sitting at the token address. Rejected.
Test Cases
Key scenarios validated by the Denim test suite:transfertoaddress(token)revertsInvalidReceiver(address(token))transferFromtoaddress(token)revertsInvalidReceiver(address(token))minttoaddress(token)revertsInvalidReceiver(address(token))batchMintwith any element targetingaddress(token)revertsInvalidReceiver(address(token))seizeWithMemowithto == address(token)revertsInvalidReceiver(address(token))seizeWithMemowithfrom == address(token)andto == treasurysucceeds- Holder self-send (
from == to) succeeds - Sends to other B20 token addresses succeed