Skip to main content
When a holder is no longer eligible, a court orders a forfeiture, or a position must be unwound, move the units to a safekeeping account with seizeWithMemo, then decide what happens to them. This guide seizes to the issuer’s treasury and cancels the units with burnWithMemo. The same first step also covers freeze-and-reissue: seize, then transfer from the treasury to a new address.
Real-world asset (RWA) tokenization is one of many use cases for the B20 Asset standard. The examples on this page use a stock token for illustration; the same flows apply to other asset types.Tokenized securities examples shown for illustration. Base is a general-purpose blockchain; issuance and compliance are the responsibility of the issuer under applicable law.

Demo

The demo uses a local browser-generated account to submit real transactions on Base Vibenet. If Vibenet or its B20 features are unavailable, it automatically switches to an illustrative offline version.
New to B20? See the B20 Token Standard for the concepts and a full launch walkthrough. These samples target base-std@1505323, viem@2.55.11, and Base Foundry v1.1.1.
seizeWithMemo is part of the Cobalt B20 surface. It is live on Base Vibenet today and reaches Base mainnet with the Cobalt hard fork. Until then, mainnet tokens only have the deprecated burnBlocked path. See the seize changelog entry.

Before You Start

You need all of the following:
  • A B20 Asset you administer, with DEFAULT_ADMIN_ROLE so you can grant roles and attach policies.
  • An account that will call seizeWithMemo and burnWithMemo. Grant it SEIZE_ROLE and BURN_ROLE. The Create an Asset Token walkthrough grants roles in the factory call; add these two if you did not.
  • A blocklist policy in the Policy Registry with the holder on it. If you gate eligibility with an allowlist, as in Restrict Eligible Holders, removing the holder from that allowlist freezes their transfers but does not make them seizable. Seize reads its own scope.
  • SEIZE and BURN not paused. Pausing TRANSFER or MINT leaves seize live.
How the seize scope works: Because the holder check is inverted, attach a blocklist to SEIZE_EXEMPT_POLICY. Accounts on the list are unauthorized and therefore seizable; every other holder stays exempt. Never attach an allowlist or ALWAYS_BLOCK to this scope: an empty allowlist authorizes nobody, so every holder would be seizable.

Seize, Cancel, and Verify

Grant the roles once per token:
Grant SEIZE_ROLE and BURN_ROLE
Then attach the blocklist, seize to the caller’s own account, and burn from there:
The seize emits, in order, Transfer(holder, treasury, amount), Memo(caller, memo), and Seized(caller, holder, treasury, amount). totalSupply is unchanged at this point. The burn then emits Transfer(treasury, address(0), amount) and Memo, and totalSupply falls. Using the same memo on both calls lets reconciliation tie the two steps to one case. burnWithMemo burns the caller’s own balance, so the account that seized must also be the destination, or you transfer from the treasury to the burner first. Recipients of a later reissue must pass TRANSFER_RECEIVER_POLICY like any other transfer. To announce the cancellation to holders and indexers, wrap the burnWithMemo in announce; see Announce a Change to Holders.
Seized appears on the first transaction and the holder balance falls by 100 EXM. After the burn, totalSupply also falls by 100 EXM.

Common Errors

These errors follow the order seizeWithMemo checks them, then the burn. A balance already sitting at the token’s own address can still be recovered: seizeWithMemo(address(token), treasury, amount, memo) is allowed. Seizing to the token’s address is not.
burnBlocked is deprecated. It destroys units directly from a holder denied under TRANSFER_SENDER_POLICY and emits no Seized event, so there is no onchain record that the units were taken rather than redeemed. Prefer seize, then burn.

See Also

Restrict Eligible Holders

Gate who can hold with policies.

Announce a Change to Holders

Disclose a treasury burn or other holder-impacting action.