{"id":"ERC-3475","name":"Abstract Storage Bonds","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-3475 models financial obligations with balances keyed by class, nonce, and amount. A single contract can represent configurable bond series and metadata; an NFT wrapper or secondary market is an application layer rather than the core token model.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-3475","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-3475","specConstraints":[{"id":"erc-3475-class-transfer-approval","constraint":"A class transfer requires isApprovedFor approval for the source, destination, and class.","appliesTo":"account-implementation","normativity":"normative","specQuote":"transferFrom MUST have the `isApprovedFor(_from, _to, _transactions[i].classId)` approval to transfer `_from` address to `_to` address for given classId (i.e for Transaction tuple corresponding to all nonces).","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Show the source, destination, class, and approval scope before a bond transfer. Do not treat a class-level approval as an unbounded approval for unrelated classes unless the implementation says so."},{"id":"erc-3475-nonce-transfer-allowance","constraint":"A nonce-specific allowance is required for transferAllowanceFrom.","appliesTo":"account-implementation","normativity":"normative","specQuote":"transferAllowanceFrom MUST have the `allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId)`","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Make both class and nonce visible in allowance reviews. Avoid presenting a nonce-specific grant as permission over an entire bond class."},{"id":"erc-3475-single-issuer","constraint":"A bond class must be issued by one issuing entity such as an authorized bank or ownable issuer.","appliesTo":"infrastructure","normativity":"normative","specQuote":"it MUST be issued by a single entity (for instance, a role-based ownable contract that has integration with the liquidity pool of the deposited collateral by `_to` address).","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Show the issuer authority and class terms as separate facts. Do not infer issuer trust or collateral quality from the class/nonce structure alone."},{"id":"erc-3475-redemption-maturity","constraint":"Redemption for a class and nonce is permitted only after the implementation's maturity conditions are met.","appliesTo":"application","normativity":"normative","specQuote":"redeem function for a given class, and nonce category MUST BE done after certain conditions for maturity (can be end time, total active liquidity, etc.) are met.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Display maturity and issuer-defined redemption conditions before enabling redeem. Do not turn a bond balance into an implied immediately withdrawable balance."},{"id":"erc-3475-burn-controller","constraint":"Burning a class and nonce is restricted to the controller contract.","appliesTo":"account-implementation","normativity":"normative","specQuote":"burn function for given class and nonce MUST BE called by only the controller contract.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Label controller-driven invalidation separately from a holder-initiated burn or redemption."},{"id":"erc-3475-approval-controller","constraint":"The contract MUST define an internal function for approval conditions; it SHOULD be callable only by the bank or owner authority.","appliesTo":"account-implementation","normativity":"normative","specQuote":"contract MUST define internal function regarding the conditions for setting approval and should be callable only by bank or owner.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Show who can create or change transfer approvals and avoid presenting an approval as user-controlled when issuer authority can override it."},{"id":"erc-3475-issue-event","constraint":"Issuing bonds emits an Issue event, excluding zero-value issuing.","appliesTo":"infrastructure","normativity":"normative","specQuote":"Issue MUST trigger when Bonds are issued. This SHOULD not include zero value Issuing.","specSection":"Specification — Events","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#events","designImplication":"Use Issue events for portfolio history and distinguish nonzero issuance from an empty administrative call."},{"id":"erc-3475-redeem-event","constraint":"Redeeming bonds emits a Redeem event, excluding zero-value redemption.","appliesTo":"infrastructure","normativity":"normative","specQuote":"Redeem MUST trigger when Bonds are redeemed. This SHOULD not include zero value redemption.","specSection":"Specification — Events","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#events","designImplication":"Use Redeem events for lifecycle history and keep zero-value calls from appearing as user redemption."},{"id":"erc-3475-burn-event","constraint":"Burn emits a Burn event when bonds are invalidated or burned, excluding zero-value burning.","appliesTo":"infrastructure","normativity":"normative","specQuote":"* @dev `Burn` MUST trigger when the bonds are being redeemed via staking (or being invalidated) by the bank contract.\n* @dev `Burn` MUST trigger when Bonds are burned. This SHOULD not include zero value burning.","specSection":"Specification — Events","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#events","designImplication":"Label controller invalidation and destruction as Burn events, not as ordinary transfer or redemption activity."},{"id":"erc-3475-transfer-event","constraint":"Transfer emits a Transfer event for bond transfers and must not use a zero-address source for issuance.","appliesTo":"infrastructure","normativity":"normative","specQuote":"* @dev Transfer MUST trigger when Bonds are transferred. This SHOULD not include zero value transfers.\n* @dev Transfer event with the _from `0x0` MUST not create this event(use `event Issued` instead).","specSection":"Specification — Events","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#events","designImplication":"Keep issuance, transfer, and redemption labels distinct in lifecycle history. Do not infer a holder-to-holder transfer from an issuance-shaped event."},{"id":"erc-3475-approval-event","constraint":"Approving an operator emits an Approval event, excluding zero-value approval.","appliesTo":"infrastructure","normativity":"normative","specQuote":"Approval MUST trigger when bond holders are approving an _operator. This SHOULD not include zero value approval.","specSection":"Specification — Events","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#events","designImplication":"Show operator approvals in the bond history and expose their class/nonce scope instead of hiding them as generic settings changes."},{"id":"erc-3475-event-indexing","constraint":"Bond lifecycle events must be emitted so clients can index Issue, Redeem, and Transfer transactions.","appliesTo":"infrastructure","normativity":"backwards-compatibility","specQuote":"To ensure the indexing of transactions throughout the bond lifecycle (i.e \"Issue\", \"Redeem\" and \"Transfer\" functions), events cited in specification section MUST be emitted when such transaction is passed.","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#backwards-compatibility","designImplication":"Treat lifecycle history as incomplete until the indexer has processed the required events, and keep class/nonce context attached to each activity item."},{"id":"erc-3475-operator-broad-authority","constraint":"setApprovalFor gives the selected operator default permission to transfer, burn, and redeem bonds.","appliesTo":"application","normativity":"security-considerations","specQuote":"The `function setApprovalFor(address _operatorAddress)` gives the operator role to `_operatorAddress`. It has all the permissions to transfer, burn and redeem bonds by default.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#security-considerations","designImplication":"Use a high-friction review for broad operator approvals and state the transfer, burn, and redeem authority plainly."},{"id":"erc-3475-scoped-approval","constraint":"A one-time allocation should use approve with a specific Transaction[] rather than broad setApprovalFor.","appliesTo":"application","normativity":"security-considerations","specQuote":"If the owner wants to give a one-time allocation to an address for specific bonds(classId,bondsId), he should call the `function approve()` giving the `Transaction[]` allocated rather than approving all the classes using `setApprovalFor`.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#security-considerations","designImplication":"Prefer a scoped approval flow for one-time allocations and show exactly which class and nonce the operator can use."},{"id":"erc-3475-market-maker-caller","constraint":"Redemption and burning SHOULD be callable only by bank or secondary-market-maker contracts.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Show the issuer or market-maker authority for redeem and burn actions, and do not offer those controls as ordinary holder operations.","specQuote":"* @dev furthermore, it SHOULD ONLY be called by the bank or secondary market maker contract."},{"id":"erc-3475-approval-call-authority","constraint":"approve() SHOULD be callable only by the bank or the account owner.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Make the approval authority explicit in bond-management UI and distinguish account-owner approval from operator permissions.","specQuote":"* @dev `approve()` should only be callable by the bank, or the owner of the account."},{"id":"erc-3475-metadata-shape","constraint":"Class and nonce metadata SHOULD follow the documented metadata structures.","appliesTo":"application","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Use the documented metadata shape for bond labels and details, while showing an unknown or incomplete state when a class or nonce response does not conform.","specQuote":"* @dev The metadata SHOULD follow a set of structures explained later in the metadata.md"},{"id":"erc-3475-values-mapping","constraint":"Class and nonce values SHOULD follow the metadata structures and map correctly to the corresponding metadata fields.","appliesTo":"application","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#specification","designImplication":"Bind displayed bond fields to their metadata keys and surface unmapped values instead of silently assigning the wrong label or unit.","specQuote":"* @dev the values SHOULD follow a set of structures as explained in metadata along with correct mapping corresponding to the given metadata structure"},{"id":"erc-3475-metadata-indexing","constraint":"The metadata key used by classValues and nonceValues SHOULD be an index.","appliesTo":"application","normativity":"normative","specSection":"Specification > 4. Decoding data","specUrl":"https://ercs.ethereum.org/ERCS/erc-3475#decoding-data","designImplication":"Treat the index as part of the decoder contract and show a clear unavailable state when a bond metadata lookup cannot resolve it.","specQuote":"This is done via overriding the function interface for functions `classValues` and `nonceValues` by defining the key (which SHOULD be an index) to read the corresponding information stored as a JSON object."}],"summary":"ERC-3475 models financial obligations with balances keyed by class, nonce, and amount. A single contract can represent configurable bond series and metadata; an NFT wrapper or secondary market is an application layer rather than the core token model.","applicability":{"whenToUse":["The deployed contract uses class/nonce/amount balances to represent financial obligations.","The issuer publishes terms that the product can display with their source.","Users need a portfolio view of multiple classes or series, with actions based on actual contract permissions."],"whenToAvoid":["The deployed contract exposes ordinary fungible or ERC-721 positions instead of ERC-3475 class/nonce balances.","The issuer does not publish bond terms or redemption conditions for the class and nonce.","No token balances, swaps, lending, or yield flows appear in the product."]},"designerTakeaways":["You can show a class/nonce balance with rate, maturity, and terms when the issuer publishes them.","You can support transfer or a secondary market only when the deployed implementation exposes it.","You can group multiple classes and nonces in one contract without treating them as ERC-721 tokens."],"problemsSolved":[{"problem":"DeFi positions are opaque","oldWay":"A balance-only view can omit issuer terms and series context","newWay":"Position = class/nonce balance with published rate, maturity, and terms","impact":"critical"},{"problem":"Can't trade DeFi positions easily","oldWay":"Withdraw, transfer tokens, re-deposit","newWay":"Transfer or list the position only when the implementation supports that policy","impact":"critical"},{"problem":"Bond series need a common way to group balances","oldWay":"Products integrate separate or custom representations for each series","newWay":"One contract can hold multiple configurable classes and nonces","impact":"high"},{"problem":"Bond terms not machine-readable","oldWay":"Check docs/UI for rate, maturity, terms","newWay":"Read class/nonce metadata when the contract publishes those terms","impact":"high"},{"problem":"Applications need structured class and series identifiers","oldWay":"Custom balance and metadata models require separate integration work","newWay":"Represent the issuer's configured classes and nonces; wrappers may add application composition","impact":"medium"}],"uxPatterns":[{"name":"Bond Portfolio Dashboard","description":"Display class/nonce balances and published bond terms","userFlow":["User views bond portfolio","Sees all classes they hold","Each shows amount and any published rate or maturity","Mark terms as issuer-provided metadata","Show only actions supported by the deployed contract"]},{"name":"Bond Issuance","description":"Issuer application flow for configuring supported class and nonce metadata","userFlow":["Issuer names bond series","Configures multiple classes","Sets supported terms, such as rate or maturity, using the issuer’s documented schema","Adds additional classes as needed","Creates bond contract with all classes"]},{"name":"Bond Purchase","description":"Buy into specific bond class","userFlow":["User browses bond series","Sees available classes with terms","Compares published terms and separately sourced risk information","Enters purchase amount","Sees any return projection with assumptions and default risk","Purchases, receives the configured class/nonce balance"]},{"name":"Bond Trading","description":"Secondary market when the deployed position is transferable","userFlow":["User browses bond marketplace","Sees listings with price/face value","Reviews any discount alongside issuer terms and default risk; a discount does not guarantee extra return","Buys position only after checking transfer and issuer rules","Or lists it when the deployed contract and marketplace support that flow"]},{"name":"Bond Maturity & Redemption","description":"Show issuer-controlled maturity and redemption status","userFlow":["A published maturity or redemption condition becomes relevant","User sees the issuer's published maturity or redemption state","Shows principal or interest only when the contract exposes those values","Explains that redeem/burn authority belongs to the issuer/controller","Refreshes after the issuer executes the contract action"]}],"uiComponents":[{"name":"BondClassCard","description":"Display issuer-published class terms and status; capacity, default, and risk are implementation/application data when available.","states":["available","full","matured","defaulted"],"props":["classId","rate","maturity","capacity","filled","riskLevel"]},{"name":"BondPosition","description":"Show a class/nonce balance with issuer-published terms and lifecycle status when available.","states":["active","matured","redeemed"],"props":["classId","nonceId","amount","purchaseDate","maturityDate","publishedTerms"]},{"name":"RiskIndicator","description":"Show separately sourced risk or seniority information; ERC-3475 does not define universal risk levels.","states":["low","medium","high"],"props":["riskLevel","seniorityRank","showExplanation"]},{"name":"YieldCalculator","description":"Project returns from published terms under explicit assumptions; this is an application calculation, not a protocol result.","states":["calculating","ready"],"props":["principal","rate","startDate","maturityDate","compounding"]},{"name":"BondMarketplace","description":"Optional application-layer secondary-market view for the selected bond contract; secondary trading is not part of the core ERC-3475 model.","states":["loading","active","no-listings"],"props":["bondContract","classFilter","listings[]","onBuy","onList"]}],"antiPatterns":[{"pattern":"Hiding bond class differences","why":"Published priority and repayment terms may differ between classes.","instead":"Explain issuer-defined seniority and repayment terms when provided; show unknown when not verified.","severity":"critical"},{"pattern":"Not showing maturity date prominently","why":"A published maturity date can be confused with guaranteed repayment or a user-triggered redemption.","instead":"Show the published maturity and actual redemption conditions separately; do not promise payment or user redemption at that time.","severity":"high"},{"pattern":"Confusing face value with market price","why":"User pays $980 for $1000 bond, doesn't understand","instead":"Show \"Face: $1000 | Price: $980 (2% discount)\"","severity":"high"},{"pattern":"Presenting estimated interest as money already received","why":"Accrual depends on issuer terms and does not guarantee payment.","instead":"Show calculated accrual only when supported by published terms; label assumptions and distinguish it from paid amounts.","severity":"medium"},{"pattern":"Hiding default risk","why":"Bonds can default, users need to understand","instead":"Show issuer and default risk. Describe protection only when verified, including its limits; otherwise state that protection is unverified.","severity":"high"}],"keyTakeaways":["ERC-3475 = class/nonce/amount balances for structured obligations","One contract can represent multiple classes; terms and risk/reward depend on issuer-published data and implementation.","Explain seniority only when the contract or issuer publishes it, and define what it means.","Show face value and any separately sourced market price clearly.","Display published yield or maturity terms when the contract exposes them"],"technicalNotes":"ERC-3475 uses a multi-dimensional token model keyed by (classId, nonceId, amount), not ERC-721 token IDs. Class and nonce metadata, rates, maturities, and redemption conditions are configurable by the implementation. issue(), redeem(), burn(), and transfer() behavior and permissions follow the deployed contract; redeem and burn are issuer/controller actions in the reference model. Use NFT language only for a separate wrapper or application layer.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-3475 official specification","url":"https://ercs.ethereum.org/ERCS/erc-3475","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-3475","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-3475","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-3475","markdown":"https://www.eipsfordesigners.com/standards/ERC-3475/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-3475/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-3475","official":"https://ercs.ethereum.org/ERCS/erc-3475","discussion":"https://ethereum-magicians.org/search?q=ERC-3475"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}