Skip to main content
Version: Next

Holder asset protection

A token or NFT on CenterChain cannot be burned or moved out of a holder's account by anyone but the holder. Holding an administrative role — the denom admin, the class issuer, or the module authority — does not let that role act on somebody else's balance or token.

This is the rule introduced in v1.1. Before it, the denom admin could burn from another address and force-transfer a balance, and a class issuer could burn any token in its class. Those paths are closed.

What the rule covers​

MessageBeforeNow
MsgForceTransferThe denom admin could move a balance to another addressAlways rejected, with ErrHolderAssetProtection (1118). The message stays in the API so a caller gets a stable error instead of "unimplemented".
MsgBurn from another address (burn_from_address ≠ sender)The denom admin could burn any holder's balanceRejected, with ErrHolderAssetProtection (1118).
MsgBurn of the sender's own balanceWorkedUnchanged. A denom admin can still burn what it holds itself.
MsgBurnNFT by the class issuer or the module authorityWorkedRejected, with ErrHolderAssetProtection (1214).
MsgBurnNFT by the token's ownerWorkedUnchanged. The owner can burn their own token.

The check is a direct comparison inside the message handler: the burn is allowed only when the address acting is the address that holds the asset. There is no capability, no role, and no module-authority exception that re-opens the path.

The two error codes​

The same rule is enforced in two modules, so it has one error code per module:

ModuleCodeIdentifier
Token factory1118ErrHolderAssetProtection
NFT1214ErrHolderAssetProtection

Both carry the message holder asset protection: the operation requires the holder's own consent. The codes are distinct because each module registers its own error space; match on the wrapped sentinel, not on the number, across modules.

What an issuer can still do​

Holder protection removes the powers that touch existing holder assets. It does not touch the powers that create supply, change policy, or gate who may receive:

Still allowedEffect
MsgMintAdds new supply. The issuer's own balance grows; no existing holder is affected.
Denom restriction, delegates, MsgChangeAdminChange who may receive the denom of a restricted denom. Circulation control, not seizure.
Blocklist / freezeAn entry stops a locked address from sending or receiving the denom. The balance stays where it is; nothing changes hands.
Class metadata, MsgChangeClassIssuerClass administration. Metadata rules still apply and immutability still binds.
MsgBurnNFT of a token the signer ownsThe signer is the holder, so the rule does not bind.

The compliance tool that remains is blocking, not seizure: an address is locked out of the denom and its assets stay put. The chain no longer offers a way to take an asset away from a holder without that holder's signature.

Holder protection does not forbid a holder from moving their own asset. It requires that the holder signs. The paths that make that explicit are:

  1. Single-leg authorizations. MsgExecutePull and MsgExecuteBurn settle against a MsgCreateAuthorization the holder signed, so the move is inside a permission the holder granted. See Two-phase execution.
  2. Swap settlement. A swap leg moves an NFT or burns fungible input under the holder's signed authorization.
  3. Claim permits. MsgClaimSend and MsgClaimSendNFT carry the holder's own signature; an executor only submits the signed message. See Claim permits.
  4. Mint and send execution. MsgExecuteSend carries a separate action permit signed by the balance holder, and MsgExecuteMint requires the denom admin's signature. The TX1 gas authorization alone cannot move an asset.

All of these are holder-signed. The rule bars acting without the holder's signature; it does not bar the holder from acting.

Module-internal paths​

One burn path is not a holder asset at all: the liquidity module burns LP shares when a position is removed. Those shares are administered by the liquidity module account, and the burn is an internal keeper call rather than MsgBurn, so the holder-protection guard does not apply. It is a different thing from burning a holder's balance.

Why the old switches are inert​

The chain still declares two capabilities that used to gate the removed powers:

CapabilityUsed to gateNow
enable_force_transferMsgForceTransferInert. The message is rejected whether or not the capability is set.
enable_burn_fromBurning from an address other than the sender'sInert. The burn-from path is rejected whether or not the capability is set.

A capability answers whether an action exists on this chain. Holder protection answers a stronger question: the action does not exist for another holder's asset at all, so the switch has nothing left to enable. See Security and governance.