{"id":"ERC-7994","name":"Purpose Bound Money","status":"Final","chain":"both","category":{"id":"defi","name":"DeFi Patterns","description":"Vaults, bonds, and financial primitives"},"journeyStages":[{"id":"management","name":"Asset Management","description":"Managing assets over time"}],"uxImpact":"ERC-7994 defines purpose-bound money through a binding that names a recipient, amount, unlock conditions, and optional expiry. Merchant, KYC, category, receipt, and spending-limit behavior comes from the condition implementations selected by the issuer; the standard does not provide a universal merchant directory or transfer pre-check.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-7994","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-7994","summary":"ERC-7994 defines purpose-bound money through a binding that names a recipient, amount, unlock conditions, and optional expiry. Merchant, KYC, category, receipt, and spending-limit behavior comes from the condition implementations selected by the issuer; the standard does not provide a universal merchant directory or transfer pre-check.","applicability":{"whenToUse":["Users need a binding with a named recipient, amount, and issuer-selected unlock conditions.","The deployment implements bindPurpose, isUnlocked, and claim with conditions the product can explain.","The product can distinguish locked, unlocked, claimed, and any implementation-specific expiry handling."],"whenToAvoid":["The deployed contract has no purpose binding or condition resolver to inspect.","The flow is an ordinary transfer with no recipient claim or unlock state.","No merchant, category, receipt, KYC, or spending-limit condition is configured for the binding.","No token balances, swaps, lending, or yield flows appear in the product."]},"designerTakeaways":["You can show the recipient, amount, unlock conditions, and expiry of each purpose binding.","You can reflect spending limits or categories when the selected condition resolver defines them.","You can show a merchant or eligibility result only when an application condition supplies that data."],"problemsSolved":[{"problem":"Recipients need a shared interface for condition-bound claims","oldWay":"Applications use custom escrow or restriction interfaces","newWay":"A recipient claims a bound amount after its configured conditions unlock","impact":"critical"},{"problem":"Corporate cards require centralized control","oldWay":"Visa/corporate card with bank managing restrictions","newWay":"Issuer-selected conditions can enforce spending limits or categories","impact":"high"},{"problem":"Benefits programs need to expose their eligibility conditions","oldWay":"Issue vouchers, verify manually, prone to fraud","newWay":"A condition resolver can expose merchant eligibility when the application provides it","impact":"high"},{"problem":"Grants require trust or complex escrow","oldWay":"Send grant money, request receipts, manual auditing","newWay":"The binding records whatever recipient and condition policy the issuer configures","impact":"medium"},{"problem":"A claim may need both unlock conditions and an expiry","oldWay":"Separate systems for spend categories and time limits","newWay":"A binding can include an optional expiry alongside its configured conditions","impact":"medium"}],"uxPatterns":[{"name":"Purpose-Bound Wallet View","description":"Show tokens with their spending restrictions","userFlow":["User opens wallet","UI separates regular vs purpose-bound tokens","Each restricted token shows its constraints","Expiry and configured conditions displayed clearly","Tap to inspect the resolver-provided eligibility details"]},{"name":"Merchant Payment Flow","description":"Pay with purpose-bound tokens at valid merchant","userFlow":["User at checkout","UI creates or loads the binding for the recipient and amount","Configured conditions shown with pending/unlocked state","User reviews the claim path","Recipient claims after isUnlocked(bindingId) is true"]},{"name":"Invalid Merchant Warning","description":"Clear feedback when tokens can't be used","userFlow":["User at non-eligible merchant","Binding shown as locked or not applicable","Clear explanation of the unresolved condition","Link to the issuer's condition or eligibility information","Alternative payment methods highlighted"]},{"name":"Expense Token with Receipt","description":"Corporate expense requiring documentation","userFlow":["User initiates expense payment","UI shows all requirements","Upload receipt if needed","Verify the configured condition and limits","Recipient claim proceeds when the binding unlocks"]}],"uiComponents":[{"name":"PurposeBoundBalance","description":"Token balance with restriction summary","states":["loading","loaded","restricted","expiring-soon"],"props":["amount","symbol","conditions","expiresAt","restrictions"]},{"name":"PurposeConditionChecker","description":"Show the deployed condition resolver's result for a binding","states":["checking","valid","invalid","unknown"],"props":["bindingId","conditions","isUnlocked"]},{"name":"SpendingLimitTracker","description":"Show issuer-condition supplied usage or limit data when present; ERC-7994 does not define universal spending limits.","states":["available","approaching-limit","at-limit"],"props":["used","limit","period","resetsAt"]},{"name":"ConditionExplorer","description":"Explore issuer-provided condition or eligibility data when available","states":["searching","results","no-results","loading"],"props":["bindingId","conditionType","issuerData"]}],"antiPatterns":[{"pattern":"Showing restricted tokens without explaining restrictions","why":"Users confused why payment fails","instead":"Show the binding's configured conditions, recipient, amount, and expiry; label merchant data as application-specific","severity":"critical"},{"pattern":"Letting users attempt invalid payments","why":"Wasted gas and frustrating experience","instead":"Check isUnlocked(bindingId) when supported before presenting the claim action","severity":"critical"},{"pattern":"Not showing expiry prominently","why":"Users may miss a claim deadline; expiry consequences depend on the deployment.","instead":"Highlight an optional binding expiry and notify users according to the issuer's policy","severity":"high"},{"pattern":"Hiding spending limits until exceeded","why":"Surprise rejections damage trust","instead":"When a configured condition defines a spending limit, show its actual usage, period, and remaining amount; otherwise omit the counter.","severity":"high"},{"pattern":"No way to find valid merchants","why":"Users have tokens but don't know where to spend them","instead":"Offer condition or merchant discovery only when issuer/application data supports it","severity":"medium"}],"specConstraints":[{"id":"condition-integrity","constraint":"Purpose-binding condition checks such as Merkle roots and timestamps must resist tampering.","appliesTo":"account-implementation","normativity":"security-considerations","specQuote":"Condition-checking mechanisms (e.g., Merkle roots, timestamps) must be secure against tampering.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-7994#security-considerations","designImplication":"Show the condition source and verification state and do not label a purpose binding valid without verified condition data."},{"id":"purpose-binding-expiry-option","constraint":"A purpose binding's expiry timestamp is OPTIONAL; when supplied, the transfer cannot be claimed after it.","appliesTo":"account-implementation","normativity":"normative","specQuote":"- An optional `expiry` timestamp after which the transfer can no longer be claimed.","specSection":"Specification > Interface","specUrl":"https://ercs.ethereum.org/ERCS/erc-7994#interface","designImplication":"Show an expiry deadline only when the binding supplies one, make the claim-unavailable state explicit after it passes, and do not imply every binding has a deadline."},{"id":"atomic-claim","constraint":"claim must verify all binding conditions atomically.","appliesTo":"application","normativity":"security-considerations","specQuote":"The `claim()` function must ensure atomic verification of all conditions.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-7994#security-considerations","designImplication":"Represent claim as all-or-nothing and explain which condition blocked the claim when atomic verification fails."},{"id":"binding-replay","constraint":"Replay attacks must be mitigated with unique binding IDs and expiration fields.","appliesTo":"application","normativity":"security-considerations","specQuote":"Replay attacks must be mitigated using unique binding IDs and expiration fields.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-7994#security-considerations","designImplication":"Show binding identity and expiry in payment/claim review and prevent reuse after expiration or successful claim."}],"keyTakeaways":["ERC-7994 binds tokens to a recipient, amount, unlock conditions, and optional expiry.","Always show configured restrictions, recipient, amount, and expiry clearly","Show binding and unlock status before presenting a claim action","Provide condition or merchant discovery only when the issuer/application supplies it"],"technicalNotes":"ERC-7994 defines purpose bindings with a recipient, amount, an array of arbitrary UnlockCondition values, and optional expiry. The core lifecycle uses bindPurpose, isUnlocked(bindingId), and claim(bindingId); merchant, KYC, whitelist, receipt, category, and spending-limit semantics belong to the condition implementations. Do not assume an isValidTransfer selector, a universal merchant list, or an ordinary transfer flow.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-7994 official specification","url":"https://ercs.ethereum.org/ERCS/erc-7994","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-7994","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-7994","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-7994","markdown":"https://www.eipsfordesigners.com/standards/ERC-7994/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-7994/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-7994","official":"https://ercs.ethereum.org/ERCS/erc-7994","discussion":"https://ethereum-magicians.org/search?q=ERC-7994"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}