{"id":"ERC-6672","name":"Multi-redeemable NFTs","status":"Final","chain":"both","category":{"id":"physical","name":"Physical & Real World","description":"Connecting NFTs to physical items"},"journeyStages":[{"id":"specialized","name":"Specialized Interactions","description":"Physical items, AI, social, gaming"}],"uxImpact":"Users can redeem the same NFT for multiple different perks across different campaigns — a concert ticket NFT might be redeemable for entry, merchandise, and a meet-and-greet separately.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-6672","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-6672","sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-6672","type":"official-spec"}],"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","summary":"Users can redeem the same NFT for multiple different perks across different campaigns — a concert ticket NFT might be redeemable for entry, merchandise, and a meet-and-greet separately.","designerTakeaways":["You can list Available redemptions as cards with operator name and status per perk.","Your history view can group by campaign with Redeemed → Shipped progression.","You can notify when new redemption opportunities attach to owned tokens."],"applicability":{"whenToUse":["Event or membership NFTs unlock multiple perks over time.","Different operators run separate redemption campaigns.","ERC-6672 tracks per-redemption state."],"whenToAvoid":["Single redeem-then-burn only.","One operator one perk.","Off-chain redemption only with no on-chain status."]},"prototypeFirst":[{"screen":"Multi-redemption dashboard","why":"Holder sees all perks at once.","covers":["3 available","Mixed status","All redeemed"],"include":["Perk cards","Status pills","Operator label","Redeem CTA each"]},{"screen":"Single perk redeem flow","why":"Each operator flow may differ.","covers":["Digital perk","Physical ship"],"include":["Operator branding","Form fields","Confirm redeem"]},{"screen":"Redemption history timeline","why":"Audit past claims across campaigns.","covers":["History list"],"include":["Date","Perk name","Status progression"]},{"screen":"New perk discovery","why":"Campaigns add redemptions post-mint.","covers":["New badge on perk"],"include":["New redemption available toast","Highlight in list"]}],"mentalModel":[{"label":"One NFT","description":"Single token holds multiple redemption rights."},{"label":"Redemption slot","description":"Independent perk with own status."},{"label":"Operator","description":"Party fulfilling specific redemption."},{"label":"Status progression","description":"Available → Redeemed → Shipped per perk."},{"label":"Exhaustion","description":"Each perk redeem once unless multi-use defined."}],"statesToDesign":[{"state":"Perk available","trigger":"Not yet redeemed.","userNeed":"Claim clearly.","designResponse":"Green Available with Redeem CTA."},{"state":"Redeemed — processing","trigger":"On-chain redeem done; fulfillment pending.","userNeed":"Track fulfillment.","designResponse":"Redeemed — preparing shipment."},{"state":"Shipped / fulfilled","trigger":"Operator completed.","userNeed":"See done.","designResponse":"Fulfilled checkmark; no CTA."},{"state":"New perk added","trigger":"Campaign update.","userNeed":"Discover new value.","designResponse":"New badge and notification."},{"state":"All perks exhausted","trigger":"Every slot used.","userNeed":"See memento value.","designResponse":"All rewards claimed summary."}],"designDecisions":[{"question":"Organize by operator or perk type?","recommendation":"Group by operator with perk subtitles.","rationale":"Support routes to operator."},{"question":"Show shipping progression?","recommendation":"Yes for physical; instant for digital.","rationale":"Different expectations per perk type."},{"question":"Discovery of new redemptions?","recommendation":"Push plus in-app New on token card.","rationale":"Post-mint campaigns add value."}],"problemsSolved":[{"problem":"One redeem burns all perks","oldWay":"Single consume destroys other rights","newWay":"Independent redemption slots per perk","impact":"high"},{"problem":"Confusion which vendor fulfills what","oldWay":"Single support email","newWay":"Operator label on each redemption card","impact":"medium"},{"problem":"Lost track of claimed perks","oldWay":"No history","newWay":"Redemption timeline per NFT","impact":"medium"}],"uxPatterns":[{"name":"Multi-Redemption List","description":"Per-perk cards with status and operator.","components":["PerkCard","StatusPill","OperatorLabel"],"userFlow":["Open NFT","See perks","Redeem one","Status updates independently"]},{"name":"Redemption History","description":"Timeline of claims across campaigns.","components":["HistoryTimeline","StatusProgress"],"userFlow":["Open history","See past redeems","Track shipping"]}],"antiPatterns":[{"pattern":"Single Redeem button for all perks","why":"Claims wrong perk or all at once","instead":"Separate CTA per perk card","severity":"critical"},{"pattern":"No operator label","why":"Support chaos","instead":"Operator name on every perk row","severity":"high"},{"pattern":"Hiding fulfilled perks","why":"User cannot prove attendance","instead":"History tab with fulfilled state","severity":"medium"}],"vocabulary":[{"use":"Available reward","avoid":"Redemption slot 2","why":"Perk language."},{"use":"Claimed","avoid":"Redemption executed","why":"User past tense."},{"use":"Fulfilled by [operator]","avoid":"Operator callback complete","why":"Plain fulfillment copy."}],"specConstraints":[{"id":"redemption-operator-scope","constraint":"An operator may update only a redemption created by that same operator; redeem and cancel use msg.sender as operator.","appliesTo":"application","normativity":"normative","specQuote":"An operator SHALL only make an update to the redemption created by itself. Therefore, the `redeem()` and `cancel()` functions do not have an `_operator` parameter, and the `msg.sender` address MUST be used as the `_operator`.","specSection":"Specification > Redeem and Cancel Functions","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#redeem-and-cancel-functions","designImplication":"Scope redemption controls and history to the creating operator and do not show another campaign’s cancel/redeem action as available."},{"id":"redemption-status-identity","constraint":"The operator, tokenId, and redemptionId combination identifies the redemption flag exposed by isRedeemed.","appliesTo":"application","normativity":"normative","specQuote":"The combination of `_operator`, `_tokenId`, and `_redemptionId` MUST be used as the key in the redemption flag key-value pairs, whose value can be accessed from the `isRedeemed()` function.","specSection":"Specification > Redemption Flag Key-Value Pairs","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#redemption-flag-key-value-pairs","designImplication":"Group status by campaign/operator and token so the same NFT can show separate redemption opportunities without collisions."},{"id":"redemption-metadata","constraint":"Redemption metadata uses operator-tokenId-redemptionId keys with status and description values.","appliesTo":"application","normativity":"normative","specQuote":"The key format for the `redemptions` key-value pairs MUST be standardized as `operator-tokenId-redemptionId`, where `operator` is the operator wallet address, `tokenId` is  the identifier of the token that has been redeemed, and `redemptionId` is the redemption identifier. The value of the key `operator-tokenId-redemptionId` is an object that contains the `status` and `description` of the redemption.","specSection":"Specification > Metadata Extension","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#metadata-extension","designImplication":"Render status and description per redemption card, preserving operator and token context in history and notifications."},{"id":"erc721-compat","constraint":"The redemption standard is compatible with ERC-721.","appliesTo":"application","normativity":"backwards-compatibility","specQuote":"## Backwards Compatibility\n\nThis standard is compatible with [ERC-721](./eip-721.md).","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#backwards-compatibility","designImplication":"Keep ordinary ERC-721 wallet/storage behavior while adding separate redemption status and operator scope."},{"id":"redemption-security","constraint":"Only the creating operator should update redemption flags, and redemption functions should be isolated from ERC-721 approval models.","appliesTo":"application","normativity":"security-considerations","specQuote":"## Security Considerations\n\nAn incorrect implementation of [ERC-6672](./eip-6672.md) could potentially allow an unauthorized operator to access redemption flags owned by other operators, creating a security risk. As a result, an unauthorized operator could cancel the redemption process managed by other operators. Therefore, it is crucial for [ERC-6672](./eip-6672.md) implementations to ensure that only the operator who created the redemption, identified using `msg.sender`, can update the redemption flag using the `redeem()` and `cancel()` functions. It is also recommended to isolate the `redeem()` and `cancel()` functions from [ERC-721](./eip-721.md) approval models.\n\nThis [ERC-6672](./eip-6672.md) token is compatible with [ERC-721](./eip-721.md), so wallets and smart contracts capable of storing and handling standard [ERC-721](./eip-721.md) tokens will not face the risk of asset loss caused by incompatible standard implementations.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#security-considerations","designImplication":"Do not inherit transfer approvals into redemption authority; show operator ownership and unauthorized-action errors explicitly."},{"id":"optional-metadata-extension","constraint":"The ERC-6672 metadata extension is OPTIONAL for smart contracts.","appliesTo":"application","normativity":"normative","specSection":"Specification > Metadata Extension","specUrl":"https://ercs.ethereum.org/ERCS/erc-6672#metadata-extension","designImplication":"Feature-detect metadata before showing names or redemption details, and keep redemption state usable when the extension is absent.","specQuote":"The **metadata extension** is OPTIONAL for [ERC-6672](./eip-6672.md) smart contracts (see \"caveats\", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent."}],"technicalNotes":"ERC-6672 tracks independent redemption states; never use one button for all perks.","relatedStandards":[{"id":"ERC-7578","relationship":"Physical asset backing complement"},{"id":"ERC-2135","relationship":"Single consumable redeem pattern"}]},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-6672","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-6672","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-6672","markdown":"https://www.eipsfordesigners.com/standards/ERC-6672/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-6672/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-6672","official":"https://ercs.ethereum.org/ERCS/erc-6672","discussion":"https://ethereum-magicians.org/search?q=ERC-6672"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}