{"id":"ERC-5192","name":"Minimal Soulbound NFTs","status":"Final","chain":"both","category":{"id":"identity","name":"Identity & Privacy","description":"Who you are on-chain, and what you choose to reveal"},"journeyStages":[{"id":"approving","name":"Approving & Permissioning","description":"Granting permissions for actions"},{"id":"management","name":"Asset Management","description":"Managing assets over time"}],"uxImpact":"ERC-5192 defines a minimal soulbound interface in which locked(tokenId) reports whether an ERC-721 token is currently non-transferable. A compliant implementation can emit Locked and Unlocked, so “soulbound” should be shown as non-transferable while locked rather than as a universal forever guarantee.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-5192","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-5192","summary":"ERC-5192 defines a minimal soulbound interface in which locked(tokenId) reports whether an ERC-721 token is currently non-transferable. A compliant implementation can emit Locked and Unlocked, so “soulbound” should be shown as non-transferable while locked rather than as a universal forever guarantee.","applicability":{"whenToUse":["A product needs to show whether an ERC-721 token is currently non-transferable.","The UI reads locked() and observes Locked/Unlocked state changes.","Issuer-defined burn and recovery rules are disclosed separately from the current lock state."],"whenToAvoid":["The token does not implement locked() and the Locked/Unlocked events.","Transferability can change and the UI cannot read state at render and action time.","The product needs ordinary trade or transfer interactions for this token."]},"designerTakeaways":["You can show the token's current locked() state and update the transfer UI when it changes.","You can label a token non-transferable while locked without promising that it is locked forever.","You can surface the issuer-defined burn and recovery policy alongside the current lock state."],"problemsSolved":[{"problem":"Wallets cannot tell whether an NFT currently blocks transfers","oldWay":"Every transfer button tries a transaction and fails","newWay":"The UI reads locked(tokenId) and detects the Locked/Unlocked events","impact":"high"},{"problem":"A token needs a clear non-transferable state","oldWay":"A marketplace guesses from collection branding","newWay":"Show the current lock state reported by the contract","impact":"high"},{"problem":"Issuers may later unlock a token","oldWay":"A permanent badge hides new transfer capability","newWay":"Re-read locked() at render and action time and update controls","impact":"medium"}],"uxPatterns":[{"name":"Lock State Display","description":"Show current locked() state and when it was last observed.","userFlow":["User opens token","App reads locked(tokenId)","Shows Locked or Unlocked badge","Explains issuer-defined burn/recovery policy"]},{"name":"Transfer Gate","description":"Gate transfer controls on the current lock state.","userFlow":["User selects transfer","App re-reads locked()","Blocks while true with clear explanation","Offers transfer when unlocked and supported"]}],"uiComponents":[{"name":"SoulboundIndicator","description":"Lock icon and label for soulbound tokens","states":["locked","unlocked","checking"],"props":["tokenId","showLabel"]},{"name":"CredentialCard","description":"Display card for non-transferable credentials","states":["verified","unverified","expired"],"props":["metadata","issuer","issuedAt"]},{"name":"TransferBlockedNotice","description":"Explanation when transfer attempted","states":["hidden","shown"],"props":["reason","onDismiss"]}],"antiPatterns":[{"pattern":"Calling every token permanently non-transferable","why":"locked() can return false and the interface includes an Unlocked event.","instead":"Say non-transferable while locked and re-check state before action.","severity":"critical"},{"pattern":"Hiding transfer or recovery UI forever","why":"A compliant implementation may transition to unlocked.","instead":"Render controls from current locked() state and issuer policy.","severity":"high"},{"pattern":"Treating soulbound as one fixed burn rule","why":"ERC-5192 only covers the lock state; burn/recovery is implementation-specific.","instead":"Show the separate issuer-defined burn and recovery rules.","severity":"medium"}],"specConstraints":[{"id":"locked-state","constraint":"When locked(tokenId) is true, all ERC-721 transfer functions for that token must throw.","appliesTo":"application","normativity":"normative","specQuote":"A token with a `uint256 tokenId` may be bound to a receiving account with `function locked(...)` returning `true`. In this case, all [EIP-721](./eip-721.md) functions of the contract that transfer the token from one account to another must throw.","specSection":"Specification > Contract Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-5192#contract-interface","designImplication":"Show non-transferable controls and a locked state based on the current token result, while keeping issuer-defined burn/recovery paths separate."},{"id":"soulbound-locked-event","constraint":"When a token is minted with locked status, the implementation SHOULD emit Locked.","appliesTo":"account-implementation","normativity":"normative","specQuote":"/// @dev If a token is minted and the status is locked, this event should be emitted.","specSection":"Specification > Contract Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-5192#contract-interface","designImplication":"Use the Locked event to update indexed token status when the initial state is locked, then confirm the current state with locked(tokenId) before presenting transfer controls."},{"id":"soulbound-unlocked-event","constraint":"When a token is minted with unlocked status, the implementation SHOULD emit Unlocked.","appliesTo":"account-implementation","normativity":"normative","specQuote":"/// @dev If a token is minted and the status is unlocked, this event should be emitted.","specSection":"Specification > Contract Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-5192#contract-interface","designImplication":"Use the Unlocked event to initialize indexed token status when the initial state is transferable, while still reading locked(tokenId) for current truth."},{"id":"soulbound-discovery","constraint":"A compliant contract must advertise the soulbound interface through ERC-165.","appliesTo":"application","normativity":"normative","specQuote":"To aid recognition that an [EIP-721](./eip-721.md) token implements \"soulbinding\" via this EIP upon calling [EIP-721](./eip-721.md)'s `function supportsInterface(bytes4 interfaceID) external view returns (bool)` with `interfaceID=0xb45a3c0e`, a contract implementing this EIP must return `true`.","specSection":"Specification > Contract Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-5192#contract-interface","designImplication":"Use interface discovery as a capability signal, then read locked(tokenId) for the actual current state."},{"id":"erc721-compat","constraint":"The soulbound interface is backward compatible with ERC-721.","appliesTo":"application","normativity":"backwards-compatibility","specQuote":"## Backwards Compatibility\n\nThis proposal is fully backward compatible with [EIP-721](./eip-721.md).","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-5192#backwards-compatibility","designImplication":"Keep familiar NFT ownership/display surfaces while making current lock state explicit."}],"keyTakeaways":["ERC-5192 reports whether a token is locked right now.","Locked blocks transfer; Unlocked can restore transferability in a compliant implementation.","Read locked() at render and action time.","Show burn and recovery policy separately from transferability."],"technicalNotes":"ERC-5192 extends ERC-721 with locked(uint256 tokenId) returns (bool) and Locked/Unlocked events. Transfers MUST throw while locked(tokenId) is true; the interface does not say that the token must remain locked forever. Read the current state and implementation-specific burn/recovery behavior.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-5192 official specification","url":"https://ercs.ethereum.org/ERCS/erc-5192","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-5192","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-5192","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-5192","markdown":"https://www.eipsfordesigners.com/standards/ERC-5192/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-5192/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-5192","official":"https://ercs.ethereum.org/ERCS/erc-5192","discussion":"https://ethereum-magicians.org/search?q=ERC-5192"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}