Skip to main content
Cobalt adds a first-class seize operation to the shared B20 interface. Issuers can reassign a holder’s balance to a destination in one admin call, gated by dedicated role, pause, and policy slots. The surface is defined on the shared IB20 interface, so both the Asset and Stablecoin variants expose the identical selectors, events, and errors. Seize replaces burnBlocked as the recommended path for regulated issuers. burnBlocked remains dialable and unchanged for backwards compatibility, but new integrations should adopt seizeWithMemo.
Cobalt is not live yet. Every seize-related selector, event topic, and error described on this page is undialable until the Cobalt hardfork activates. Only the Beryl surface is available on-chain today.

When to use seize

Reach for seizeWithMemo when you need to move a holder’s balance under administrative authority — for example, to comply with a court order, recover funds from a compromised account, or move balances off a sanctioned address. Seize is a transfer, not a burn: totalSupply is unchanged and the balance moves from the holder to a destination you specify (typically a treasury or self address). If you want to destroy the seized supply, seize to a treasury first and then call burn from the treasury. This two-step flow reproduces the outcome of the old burnBlocked path with clearer accounting.

seizeWithMemo

seizeWithMemo reassigns amount from from to to as an admin operation. It skips allowance checks and the transfer policies (TRANSFER_SENDER_POLICY, TRANSFER_RECEIVER_POLICY, TRANSFER_EXECUTOR_POLICY), and enforces only the new seize policies described below. The call emits three events, in order:
  1. Transfer(from, to, amount)
  2. Memo(caller, memo) — a memo of bytes32(0) is permitted
  3. Seized(caller, from, to, amount)

Requirements

Guards are evaluated in the order above. When several would fail, the earlier revert wins: AccountNotSeizable before PolicyForbids(SEIZE_RECEIVER_POLICY, ...) before InsufficientBalance.

Roles and pause

Cobalt introduces one role and one pause feature dedicated to seize. Neither overlaps with the existing burn surface. PausableFeature is append-only. Cobalt appends SEIZE after the existing Beryl ordinals (TRANSFER=0, MINT=1, BURN=2), and ALL_FEATURES_PAUSED becomes 15 (0b1111). Because SEIZE and BURN are independent bits, pausing BURN does not stop seizeWithMemo, and pausing SEIZE does not stop burn, burnWithMemo, or burnBlocked.

Policies

Seize adds two new policy scopes. Both point into the shared PolicyRegistry via a uint64 policy ID and are written through updatePolicy(scope, policyId). SEIZE_HOLDER_POLICY inverts the usual sense: a holder must be denied by the policy to be eligible for seize. This lets an issuer maintain a BLOCKLIST of seizable accounts distinct from its transfer-blocked set.

Seize is opt-in per token

Every policy scope defaults to ALWAYS_ALLOW at token creation. Because SEIZE_HOLDER_POLICY defaults to always-allow, no account is seizable until an issuer configures the scope. Every seizeWithMemo call reverts AccountNotSeizable(from) on a token that has not set SEIZE_HOLDER_POLICY. Issuers that never configure SEIZE_HOLDER_POLICY have, in effect, no seize capability on that token. SEIZE_RECEIVER_POLICY left unset mirrors MINT_RECEIVER_POLICY: it permits any destination. A treasury does not need to be allowlisted until an issuer opts into restricting seize destinations.
seizeWithMemo and burnBlocked read different policies. seizeWithMemo reads SEIZE_HOLDER_POLICY; burnBlocked reads TRANSFER_SENDER_POLICY. A “transfer-blocked” account is not automatically “seizable” — an issuer must configure SEIZE_HOLDER_POLICY explicitly.

Events and errors

burnBlocked is deprecated

burnBlocked(from, amount) keeps its Beryl behavior at Cobalt — same selector, same events, same guards. It remains callable indefinitely for backwards compatibility, but new integrations should use seizeWithMemo. For reference, burnBlocked still:
  • Destroys amount from a from that is denied by TRANSFER_SENDER_POLICY, without spending an allowance.
  • Emits Transfer(from, address(0), amount) and BurnedBlocked(caller, from, amount). It does not emit Memo.
  • Is gated by BURN_BLOCKED_ROLE and the BURN pause vector.
  • Reverts AccountNotBlocked(from) when from is authorized under TRANSFER_SENDER_POLICY.

Migrating off burnBlocked

Replace a burnBlocked(from, amount) call with a seize to a treasury, followed by a burn from that treasury if you want the supply destroyed:
The migration crosses two role, pause, and policy domains:
  • The seize step needs SEIZE_ROLE, an unpaused SEIZE vector, from denied by SEIZE_HOLDER_POLICY, and treasury authorized by SEIZE_RECEIVER_POLICY.
  • The burn step needs BURN_ROLE on the treasury and an unpaused BURN vector.
It is not a drop-in selector swap. Grant the new role, configure SEIZE_HOLDER_POLICY to designate seizable accounts, and update your admin runbooks before switching over.

Selectors

Selectors and topic0 values below match the frozen Cobalt ABI. Beryl symbols keep their exact 4-byte selectors and topic0 values at Cobalt.

Functions

Events

Errors