{"id":"ERC-7631","name":"Dual Nature Token Pair","status":"Final","chain":"both","category":{"id":"nft","name":"NFT Capabilities","description":"What NFTs can do beyond basic ownership"},"journeyStages":[{"id":"asset-display","name":"Asset Discovery & Display","description":"Seeing tokens, NFTs, and balances"}],"uxImpact":"ERC-7631 links an ERC-20 base and ERC-721 mirror pair and standardizes how the relationship is queried. Synchronization, minting, ratios, and transfer policy remain implementation-specific; optional skip-NFT methods may be unavailable.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-7631","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-7631","specConstraints":[{"id":"rarity-metadata-reveal","constraint":"Dual-nature collections should reveal NFT metadata in a way that is not easily gameable through transfers and scraping.","appliesTo":"application","normativity":"security-considerations","specQuote":"### Rare NFT Sniping\n\nFor dual nature collections that offer ERC-721 tokens with differing rarity levels, the ERC-721 metadata should be revealed in a way that is not easily gameable with metadata scraping and ERC-20 token transfers. A recommendation is to require that an ERC-721 token is held by the same account for some time before revealing its metadata.","specSection":"Security Considerations — Rare NFT Sniping","specUrl":"https://ercs.ethereum.org/ERCS/erc-7631#rare-nft-sniping","designImplication":"Choose a reveal rule that accounts for ERC-20 movement and holding time. Do not expose rare metadata before the product’s anti-sniping condition is met."},{"id":"mirror-transfer-gas","constraint":"ERC-20 transfers can trigger O(n) ERC-721 synchronization work and must account for out-of-gas denial-of-service risk.","appliesTo":"application","normativity":"security-considerations","specQuote":"### Out-of-gas Denial of Service\n\nTransferring ERC-20 tokens can automatically initiate the minting, transferring, or burning of multiple ERC-721 tokens. This can incur O(n) gas costs instead of the typical O(1) gas costs for ERC-20 tokens transfers. Logic for selecting ERC-721 token IDs can also incur additional gas costs. Synchronization logic must consider ERC-721 related gas costs to prevent out-of-gas denial of service issues.","specSection":"Security Considerations — Out-of-gas Denial of Service","specUrl":"https://ercs.ethereum.org/ERCS/erc-7631#out-of-gas-denial-of-service","designImplication":"Show a fee and failure state for synchronization-heavy transfers. Do not promise ERC-20-like constant cost when many NFTs may move."}],"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-7631","type":"official-spec"}],"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","summary":"ERC-7631 links an ERC-20 base and ERC-721 mirror pair and standardizes how the relationship is queried. Synchronization, minting, ratios, and transfer policy remain implementation-specific; optional skip-NFT methods may be unavailable.","designerTakeaways":["You can show the implementation's token-to-NFT ratio after querying its synchronization rules.","Your settings can offer Skip NFT mint only after detecting the optional skip extension and a successful capability check.","You can display linked NFT and token together in one portfolio row."],"applicability":{"whenToUse":["Project issues both fungible and NFT representation.","The paired implementation documents automatic sync or mint behavior.","getSkipNFT/setSkipNFT are supported and do not revert for the connected account."],"whenToAvoid":["Separate unrelated ERC-20 and ERC-721.","Pair sync not implemented.","Users only ever interact with one side."]},"prototypeFirst":[{"screen":"Dual asset portfolio row","why":"Users must see both legs of pair.","covers":["Both held","Token only skipped NFT","NFT only"],"include":["Linked icon","Token balance","NFT thumbnail","Ratio label"]},{"screen":"Purchase with skip-NFT toggle","why":"Gas vs collectibility decision at checkout.","covers":["Skip on","Skip off"],"include":["Toggle with cost estimate","What you miss copy","Confirm purchase"]},{"screen":"Marketplace dual listing","why":"Buyers see both representations.","covers":["Listed as pair","Partial listing"],"include":["Pair badge","Conversion ratio","Linked item links"]},{"screen":"Sync status indicator","why":"When pair desyncs users panic.","covers":["Synced","NFT skipped","Pending mint"],"include":["Sync chip","Mint pending state","Link to mint NFT"]}],"mentalModel":[{"label":"Dual nature","description":"One economic position, two on-chain representations."},{"label":"Conversion ratio","description":"Implementation-defined relationship maps token balances or transfers to NFT behavior."},{"label":"Skip NFT","description":"Optional extension lets an account request that ERC-721 mints or transfers be skipped; calls may revert when unavailable."},{"label":"Auto-mint","description":"Some implementations synchronize ERC-20 actions with ERC-721 minting or transfers; the standard leaves that logic out of scope."},{"label":"Pair sync","description":"Burning or transferring may affect both sides — read rules."}],"statesToDesign":[{"state":"Fully synced pair","trigger":"The paired implementation reports matching token and NFT state.","userNeed":"See unified position.","designResponse":"Single row with both previews."},{"state":"Skipped NFT","trigger":"A supported skip-NFT extension accepted the user's skip setting.","userNeed":"Know can mint later or not.","designResponse":"Tokens only with a Mint collectible CTA if the paired implementation supports it."},{"state":"Pending NFT mint","trigger":"The paired implementation reports an NFT mint or synchronization in progress.","userNeed":"Track mint progress.","designResponse":"Minting collectible status strip."},{"state":"Ratio display at purchase","trigger":"Checkout.","userNeed":"Predict what they receive.","designResponse":"Show the implementation-reported token and NFT outcome after querying its rules."},{"state":"Partial side only in wallet","trigger":"Indexer missed one leg.","userNeed":"Not think asset lost.","designResponse":"Loading linked asset or manual refresh."}],"designDecisions":[{"question":"Prominent dual nature or abstract?","recommendation":"Show pair on detail; simplify to primary asset in grid.","rationale":"Grid clutter if always dual."},{"question":"Default skip-NFT on or off?","recommendation":"Off for collectors; remember user preference.","rationale":"Gas savings vs collectibility is personal."},{"question":"Marketplace show one or two listings?","recommendation":"Single listing with pair badge linking both.","rationale":"Split listings confuse price discovery."}],"problemsSolved":[{"problem":"Users unaware of linked NFT","oldWay":"Only token shows in wallet","newWay":"Dual row shows both representations","impact":"high"},{"problem":"Unexpected NFT mint gas","oldWay":"Surprise second transaction","newWay":"Skip-NFT toggle at purchase with estimate when the optional extension is supported","impact":"high"},{"problem":"Marketplace lists only one side","oldWay":"Buyer misses collectible component","newWay":"Pair badge with ratio on listing","impact":"medium"}],"uxPatterns":[{"name":"Dual Asset Row","description":"Portfolio entry showing linked token and NFT.","components":["PairLink","TokenBalance","NFTThumb","RatioLabel"],"userFlow":["Load portfolio","Pair detected","Show unified row","Expand for both"]},{"name":"Skip NFT Toggle","description":"Optional checkout control to request skipping collectible minting when the pair supports it.","components":["SkipToggle","GasEstimate","TradeoffCopy"],"userFlow":["User buys","Checks skip capability, then toggles skip when supported","Sees savings","Confirms","Receives tokens only"]}],"antiPatterns":[{"pattern":"Showing only ERC-20 after paired purchase","why":"Users think NFT failed to mint","instead":"Dual row or mint pending status","severity":"high"},{"pattern":"Skip-NFT hidden in advanced","why":"Surprise gas for unwanted mint","instead":"Show a checkout toggle only after detecting the optional extension; treat a revert as unavailable","severity":"high"},{"pattern":"Separate listings without link","why":"Price arbitrage confusion","instead":"Single pair listing with ratio","severity":"medium"}],"vocabulary":[{"use":"Linked collectible","avoid":"ERC-721 side of pair","why":"Collectible language."},{"use":"Configured conversion rate","avoid":"Standard sync ratio","why":"The pair's implementation defines the relationship."},{"use":"Request no collectible mint","avoid":"Assume setSkipNFT is available","why":"The skip extension is optional and may revert."}],"technicalNotes":"ERC-7631 links an ERC-20 base and ERC-721 mirror through mirrorERC721() and baseERC20(). It explicitly leaves synchronization logic, ratios, minting, rebasing, and transfer policy out of scope. getSkipNFT() and setSkipNFT() are optional and MAY revert; treat a revert as unavailable and hide the control. Show a conversion or mint outcome only after reading the target implementation's rules.","relatedStandards":[{"id":"ERC-20","relationship":"Fungible leg of pair"},{"id":"ERC-721","relationship":"NFT leg of pair"}]},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-7631","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-7631","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-7631","markdown":"https://www.eipsfordesigners.com/standards/ERC-7631/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-7631/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-7631","official":"https://ercs.ethereum.org/ERCS/erc-7631","discussion":"https://ethereum-magicians.org/search?q=ERC-7631"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}