{"id":"ERC-3525","name":"Semi-Fungible Token","status":"Final","chain":"both","category":{"id":"defi","name":"DeFi Patterns","description":"Vaults, bonds, and financial primitives"},"journeyStages":[{"id":"asset-display","name":"Asset Discovery & Display","description":"Seeing tokens, NFTs, and balances"}],"uxImpact":"ERC-3525 creates \"semi-fungible tokens\" - NFT-like positions with both an ID (uniqueness) and a value (quantity). This can model bonds, invoices, DeFi positions, and structured products where unique tokens also represent amounts.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-3525","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-3525","summary":"ERC-3525 creates \"semi-fungible tokens\" - NFT-like positions with both an ID (uniqueness) and a value (quantity). This can model bonds, invoices, DeFi positions, and structured products where unique tokens also represent amounts.","specConstraints":[{"id":"erc3525-interface-compatibility","constraint":"Every ERC-3525 contract must implement the ERC-3525, ERC-721, and ERC-165 interfaces.","appliesTo":"account-implementation","normativity":"normative","specQuote":"**Every [ERC-3525](./eip-3525.md) compliant contract must implement the ERC-3525, ERC-721 and [ERC-165](./eip-165.md) interfaces**","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Detect the required interfaces before showing semi-fungible value controls, and retain ERC-721 identity and ERC-165 support in compatibility checks."},{"id":"value-transfer-event","constraint":"TransferValue must be emitted when value moves between same-slot tokens, including zero-value, mint, and burn value transfers.","appliesTo":"application","normativity":"normative","specQuote":"* @dev MUST emit when value of a token is transferred to another token with the same slot,","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Use TransferValue as the value-movement signal while keeping token ownership and slot identity visible separately."},{"id":"value-approval-event","constraint":"ApprovalValue must be emitted when a token's value approval is set or changed.","appliesTo":"application","normativity":"normative","specQuote":"* @dev MUST emit when the approval value of a token is set or changed.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Refresh per-token value allowances from ApprovalValue events and show their scope independently from whole-token approvals."},{"id":"slot-change-event","constraint":"SlotChanged must be emitted when a token's slot is set or changed.","appliesTo":"application","normativity":"normative","specQuote":"* @dev MUST emit when the slot of a token is set or changed.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Treat slot changes as identity metadata changes and update category labels and value-transfer compatibility from the event."},{"id":"approval-authority","constraint":"Any approving function must be called by the owner or by an authority approved at a higher level.","appliesTo":"account-implementation","normativity":"normative","specQuote":"- For any approving function, the caller MUST be the owner or has been approved with a higher level of authority.","specSection":"Specification / Token Manipulation / Rules","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#rules","designImplication":"Show which approval level authorizes a change and require a matching owner or delegated authority in approval confirmation."},{"id":"id-transfer-authority","constraint":"ID-to-ID value transfer requires the current owner, an authorized operator, or an operator approved for the whole token or sufficient value.","appliesTo":"account-implementation","normativity":"normative","specQuote":"Caller MUST be the current owner, an authorized operator or an operator who has been","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Explain whether a transfer is authorized by ownership, operator status, token approval, or value approval before signing."},{"id":"id-transfer-validity","constraint":"ID-to-ID value transfer must reject zero or nonexistent source and destination token IDs.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Validate both token identities before showing a value transfer as executable and distinguish a missing token from an empty-value token."},{"id":"same-slot-transfer","constraint":"ID-to-ID value transfer must reject tokens whose slots do not match.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Show both slot labels in the transfer flow and block cross-category value movement before submission."},{"id":"id-transfer-amount","constraint":"ID-to-ID value transfer must reject an amount greater than the source balance or the operator's allowance.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Show spendable value and delegated value allowance separately, and prevent an amount input from exceeding either bound."},{"id":"address-transfer-receiving-token","constraint":"ID-to-address value transfer must find or create a token owned by the recipient with the source token's slot.","appliesTo":"account-implementation","normativity":"normative","specQuote":"* @dev This function MUST create a new ERC-3525 token with the same slot for `_to`,","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Preview the destination token identity and slot, including whether the transfer creates a new token, before confirmation."},{"id":"address-transfer-authority","constraint":"ID-to-address value transfer requires the source token owner, an authorized operator, or an operator approved for the whole token or sufficient value.","appliesTo":"account-implementation","normativity":"normative","specQuote":"- MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it.","specSection":"Specification / Token Manipulation / Rules","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#rules","designImplication":"Explain the authorization basis for a value-to-address transfer and keep it separate from the destination token that will receive the value."},{"id":"address-transfer-validity","constraint":"ID-to-address value transfer must reject a zero or nonexistent source token ID.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if `_fromTokenId` is zero token id or does not exist.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Validate the source token before showing a value-to-address transfer as executable."},{"id":"address-transfer-recipient","constraint":"ID-to-address value transfer must reject the zero address recipient.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if `_to` is zero address.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Make the destination address and any resulting token identity explicit, and block an unrecoverable zero-address destination."},{"id":"address-transfer-amount","constraint":"ID-to-address value transfer must reject an amount greater than the source balance or operator allowance.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Show spendable value and delegated allowance before a value-to-address transfer is signed."},{"id":"address-transfer-events","constraint":"ID-to-address value transfer must emit both Transfer and TransferValue events.","appliesTo":"application","normativity":"normative","specQuote":"*  MUST emit `Transfer` and `TransferValue` events.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Reconcile both token-ownership and value-movement records so a newly created destination token is not mistaken for a simple balance update."},{"id":"slot-approval-event","constraint":"Slot-level approval changes must emit an approval-for-slot event.","appliesTo":"application","normativity":"normative","specQuote":"* @dev MUST emit when an operator is approved or disapproved to manage all of `_owner`'s","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Show slot-wide delegation as a distinct approval scope and update it when the slot approval event is emitted."},{"id":"slot-approval-method-event","constraint":"The slot approval method must emit its ApprovalSlot event.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST emit ApprovalSlot event.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Do not mark a slot-wide approval change complete until the implementation's approval event has been observed."},{"id":"receiver-callback-check","constraint":"An ERC-3525 contract must check whether the recipient contract implements onERC3525Received.","appliesTo":"account-implementation","normativity":"normative","specQuote":"An ERC-3525 smart contract MUST check whether this function is implemented by the recipient contract","specSection":"Specification / ERC-3525 Token Receiver","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-token-receiver","designImplication":"Detect recipient callback support before completing a contract-to-contract value transfer and explain unsupported-receiver outcomes."},{"id":"receiver-callback-call","constraint":"When the recipient implements onERC3525Received, the token contract must call it after the value transfer.","appliesTo":"account-implementation","normativity":"normative","specQuote":"* @dev An ERC-3525 smart contract MUST check whether this function is implemented by the recipient contract, if the\n     *  recipient contract implements this function, the ERC-3525 contract MUST call this function after a \n     *  value transfer (i.e. `transferFrom(uint256,uint256,uint256,bytes)`).","specSection":"Specification / ERC-3525 Token Receiver","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-token-receiver","designImplication":"Treat the callback as part of the receiver contract's acceptance flow and preserve transfer state until its response is handled."},{"id":"receiver-acceptance-magic","constraint":"A recipient accepting the transfer must return 0x009ce20b.","appliesTo":"account-implementation","normativity":"normative","specQuote":"*  MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,\n     *  uint256,bytes)'))`) if the transfer is accepted.\n     *  MUST revert or return any value other than 0x009ce20b if the transfer is rejected.","specSection":"Specification / ERC-3525 Token Receiver","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-token-receiver","designImplication":"Show a callback acceptance state only for the specified magic value and treat any other response as rejected."},{"id":"receiver-rejection","constraint":"A recipient rejecting the transfer must revert or return a value other than 0x009ce20b.","appliesTo":"account-implementation","normativity":"normative","specQuote":"MUST revert or return any value other than 0x009ce20b if the transfer is rejected.","specSection":"Specification / ERC-3525 Token Receiver","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-token-receiver","designImplication":"Keep rejected contract transfers visibly failed and do not report value delivery before the receiver response is accepted."},{"id":"slot-intrinsic-properties","constraint":"When a slot value is generated from hashed properties, every property used in the calculation should be marked is_intrinsic TRUE.","appliesTo":"application","normativity":"normative","specQuote":"if a property is used in the calculation, is_intrinsic must be TRUE.","specSection":"Specification / Metadata / ERC-3525 Metadata URI JSON Schema","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-metadata-uri-json-schema","designImplication":"Expose intrinsic slot properties as identity-defining metadata and distinguish them from descriptive fields that can change without changing the slot."},{"id":"slot-property-order","constraint":"An intrinsic property should include its order in the slot calculation method.","appliesTo":"application","normativity":"normative","specQuote":"If is_intrinsic is TRUE, it must be the order of this property appeared in the calculation method of the slot.","specSection":"Specification / Metadata / ERC-3525 Metadata URI JSON Schema","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-metadata-uri-json-schema","designImplication":"Show slot-property order when explaining how a category identifier was derived, and avoid implying that unordered metadata hashes are interchangeable."},{"id":"receiver-check-rationale","constraint":"The rationale treats recipient callback checking and notification as the intended value-transfer acceptance model.","appliesTo":"application","normativity":"rationale","specQuote":"All ERC-3525 contracts MUST check for the existence of `onERC3525Received` on the recipient contract and call the function when it exists;","specSection":"Rationale / Design decision: Notification/acceptance mechanism instead of 'Safe Transfer'","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#design-decision-notificationacceptance-mechanism-instead-of-safe-transfer","designImplication":"Keep callback acceptance visible in product explanations while preserving that this quote is rationale rather than a separate normative section."},{"id":"receiver-response-rationale","constraint":"The rationale describes 0x009ce20b as the accepted callback response and any other value as rejection.","appliesTo":"application","normativity":"rationale","specQuote":"Any smart contract can implement `onERC3525Received` function for the purpose of being notified after receiving values; this function MUST return 0x009ce20b","specSection":"Rationale / Design decision: Notification/acceptance mechanism instead of 'Safe Transfer'","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#design-decision-notificationacceptance-mechanism-instead-of-safe-transfer","designImplication":"Use the callback result to explain acceptance and rejection without presenting the rationale as a new independent interface rule."},{"id":"approval-level-separation-rationale","constraint":"Approval of a token ID can authorize partial value transfers, but value approval does not authorize transfer of the token entity.","appliesTo":"application","normativity":"rationale","specQuote":"Approval of an id will lead to the ability to partially transfer values from this id by the approved operator; this will simplify the value approval for an id. However, the approval of total values in a token should not lead to the ability to transfer the token entity by the approved operator.","specSection":"Rationale / Design decision: Relationship between different approval models","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#design-decision-relationship-between-different-approval-models","designImplication":"Separate token-identity controls from value controls in approval UI and never describe a value allowance as permission to transfer the whole token."},{"id":"erc721-backwards-compatibility","constraint":"ERC-3525 is backward compatible with ERC-721.","appliesTo":"application","normativity":"backwards-compatibility","specQuote":"As mentioned in the beginning, this EIP is backward compatible with ERC-721.","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#backwards-compatibility","designImplication":"Retain ERC-721 ownership and wallet presentation paths while adding explicit slot and value views."},{"id":"approval-isolation","constraint":"Value-level and optional slot-level approval must remain isolated from ERC-721 approval models.","appliesTo":"account-implementation","normativity":"security-considerations","specQuote":"The value level approval and slot level approval (optional) is isolated from ERC-721 approval models, so that approving value should not affect ERC-721 level approvals. Implementations of this EIP must obey this principle.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#security-considerations","designImplication":"Show separate approval scopes and warn before granting value or slot authority; never imply that a value approval grants NFT-level transfer authority."},{"id":"erc721-wallet-safety","constraint":"ERC-721-compatible wallets and contracts should not risk asset loss merely from incompatible standard handling of ERC-3525 tokens.","appliesTo":"wallet","normativity":"security-considerations","specQuote":"Since this EIP is ERC-721 compatible, any wallets and smart contracts that can hold and manipulate standard ERC-721 tokens will have no risks of asset loss for ERC-3525 tokens due to incompatible standards implementations.","specSection":"Security Considerations","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#security-considerations","designImplication":"Preserve standard ERC-721 holdings and transfer affordances while labeling unsupported value operations instead of treating the token as an ordinary fungible balance."},{"id":"slot-enumeration-availability","constraint":"The slot enumeration extension is OPTIONAL and MAY publish the full list of slots for discovery.","appliesTo":"application","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Feature-detect slot enumeration before offering complete slot browsing; otherwise label discovery as unavailable.","specQuote":"The slot's enumeration extension is OPTIONAL. This allows your contract to publish its full list of `SLOT`s and make them discoverable."},{"id":"slot-approval-availability","constraint":"Slot-level approval is OPTIONAL; it allows an implementation to support approval for all tokens in a slot.","appliesTo":"application","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Feature-detect slot-wide authority and show it as a separate approval scope from whole-token, token-ID, and value approval.","specQuote":"The slot level approval is OPTIONAL. This allows any contract that wants to support approval for slots, which allows an operator to manage one's tokens with the same slot."},{"id":"approval-caller-recommendation","constraint":"The caller of an approval function SHOULD be the owner or an operator authorized through the approval hierarchy.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#specification","designImplication":"Keep the recommended caller authority visible in approval confirmation without presenting SHOULD as an unconditional protocol MUST.","specQuote":"* @dev Caller SHOULD be `_owner` or an operator who has been authorized through"},{"id":"receiver-interface-availability","constraint":"A receiving contract that wants notification SHOULD implement all functions in IERC3525Receiver and may accept or reject the transfer.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification > ERC-3525 Token Receiver","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-token-receiver","designImplication":"Show whether the recipient exposes the receiver interface and make callback acceptance or rejection explicit.","specQuote":"If a smart contract wants to be informed when they receive values from other addresses, it should implement all of the functions in the `IERC3525Receiver` interface, in the implementation it can decide whether to accept or reject the transfer. See \"Transfer Rules\" for further detail."},{"id":"approval-scope-levels","constraint":"The approval interface SHOULD distinguish full-token, OPTIONAL slot-level, token-ID, and value-level scopes, each indicating the authority it grants.","appliesTo":"application","normativity":"normative","specSection":"Specification > Token Manipulation > Rules","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#rules","designImplication":"Render full-token, slot, token-ID, and value approvals as separate scopes, feature-detecting the optional slot scope and preserving the SHOULD recommendation.","specQuote":"- `setApprovalForAll`, compatible with ERC-721, SHOULD indicate the full level of approval, which means that the authorized operators are capable of managing all the tokens, including their values, owned by the owner.\n- `setApprovalForSlot` (optional) SHOULD indicate the slot level of approval, which means that the authorized operators are capable of managing all the tokens with the specified slot, including their values, owned by the owner.\n- The token ID level `approve` function, compatible with ERC-721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner.\n- The value level `approve` function, SHOULD indicate that the authorized operator is capable of managing the specified maximum value of the specified token owned by the owner."},{"id":"id-transfer-semantics","constraint":"The ID-to-ID transfer function SHOULD indicate value transfer from one token to another under the stated transfer rules.","appliesTo":"application","normativity":"normative","specSection":"Specification > Token Manipulation > Rules","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#rules","designImplication":"Label the action as value movement between two token identities and keep same-slot and authorization conditions visible.","specQuote":"- The `transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value)` function, SHOULD indicate value transfers from one token to another token, in accordance with the rules below:"},{"id":"address-transfer-semantics","constraint":"The ID-to-address transfer function SHOULD indicate value transfer from one token ID to an address under the stated transfer rules.","appliesTo":"application","normativity":"normative","specSection":"Specification > Token Manipulation > Rules","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#rules","designImplication":"Explain that the recipient receives value through a token identity, including the returned destination token ID when available.","specQuote":"- The `transferFrom(uint256 _fromTokenId, address _to, uint256 _value)` function, which transfers value from one token ID to an address, SHOULD follow the rule below:"},{"id":"metadata-extension-availability","constraint":"The optional metadata interface can be identified through ERC-165 interface detection.","appliesTo":"application","normativity":"normative","specSection":"Specification > Metadata > Metadata Extensions","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#metadata-extensions","designImplication":"Feature-detect the metadata extension before rendering richer contract, slot, or token descriptions.","specQuote":"This optional interface can be identified with the ERC-165 Standard Interface Detection."},{"id":"metadata-json-uri","constraint":"The metadata extension's contractURI function should return JSON using the data:application/json; header.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification > Metadata > Metadata Extensions","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#metadata-extensions","designImplication":"Parse supported contract metadata as data, label its source, and handle unavailable or invalid metadata without assuming that a JSON descriptor proves trust.","specQuote":"* @dev This function SHOULD return the URI for this contract in JSON format, starting with\n     *  header `data:application/json;`."},{"id":"metadata-slot-json-uri","constraint":"The metadata extension's slotURI function should return JSON using the data:application/json; header.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification > Metadata > Metadata Extensions","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#metadata-extensions","designImplication":"Use the selected slot's metadata for its description when available. Handle missing or invalid JSON explicitly rather than substituting another slot's description.","specQuote":"* @dev This function SHOULD return the URI for `_slot` in JSON format, starting with header\n     *  `data:application/json;`."},{"id":"metadata-direct-json-recommendation","constraint":"The metadata extension recommends returning contract, slot, and token metadata directly as JSON instead of centralized-resource URLs.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification > Metadata > Metadata Extensions","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#metadata-extensions","designImplication":"Identify the actual metadata source and its availability. Treat direct JSON as a recommendation, not a guarantee that every collection stores its metadata onchain.","specQuote":"*  Because of the higher reliability of data stored in smart contracts compared to data stored in\n *  centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and\n *  `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing\n *  to any resource stored in a centralized system."},{"id":"metadata-source-rationale","constraint":"The rationale strongly recommends generating metadata directly from contract code instead of giving out an off-chain server URL.","appliesTo":"application","normativity":"rationale","specSection":"Rationale > Metadata generation","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#metadata-generation","designImplication":"Present metadata provenance and transparency signals without turning this rationale into an unconditional contract requirement.","specQuote":"This token standard is designed to represent semi-fungible assets, which are most suited for financial instruments rather than collectibles or in-game items. For maximum transparency and safety of digital assets, we strongly recommend that all implementations should generate metadata directly from contract code rather than giving out an off-chain server URL."},{"id":"value-decimal-display","constraint":"Metadata describes the number of decimal places that a token value SHOULD display.","appliesTo":"application","normativity":"normative","specSection":"Specification > Metadata > ERC-3525 Metadata URI JSON Schema","specUrl":"https://ercs.ethereum.org/ERCS/erc-3525#erc-3525-metadata-uri-json-schema","designImplication":"Use the declared decimal places to show human-readable values while retaining a raw-value fallback when metadata is missing.","specQuote":"\"description\": \"The number of decimal places that the balance should display - e.g. 18, means to divide the token value by 1000000000000000000 to get its user representation.\""}],"applicability":{"whenToUse":["Your product addresses: nFTs can't represent partial amounts.","Your product addresses: eRC-1155 can't track per-token attributes.","The flow should deliver: sFT has ID + value. Token #123 can hold 50 units, split into two 25-unit tokens.","You are designing a bond portfolio display experience with visible states and recovery paths."],"whenToAvoid":["The product has no token-level value or slot compatibility to display.","The deployed contract does not implement the ERC-3525 value-transfer or approval interface.","Users only need ordinary ERC-721 ownership and no partial-value operations.","No token balances, swaps, lending, or yield flows appear in the product."]},"designerTakeaways":["Design the flow to support sFT has ID + value. Token #123 can hold 50 units.","You can each SFT ID is unique but shares \"slot\" (category) with similar tokens in the interface.","Design the flow to support one SFT represents unique position with value in single token."],"problemsSolved":[{"problem":"NFTs can't represent partial amounts","oldWay":"Own 100% of an NFT or nothing. Can't own \"50 units of Bond #123\"","newWay":"SFT has ID + value. Token #123 can hold 50 units, split into two 25-unit tokens","impact":"critical"},{"problem":"ERC-1155 can't track per-token attributes","oldWay":"All token ID 5 are identical. Can't have Bond #5 with different maturity dates","newWay":"Each SFT ID is unique but shares \"slot\" (category) with similar tokens","impact":"high"},{"problem":"Complex DeFi positions need multiple tokens","oldWay":"Represent bond position with ERC-721 for ID + ERC-20 for value","newWay":"One SFT represents unique position with value in single token","impact":"high"},{"problem":"Can't merge or split positions easily","oldWay":"Burning and reminting required to combine positions","newWay":"Native transfer value between same-slot tokens, or split/merge","impact":"medium"},{"problem":"Invoice/receivable tokenization is awkward","oldWay":"Invoice as NFT can't be partially assigned or valued","newWay":"Invoice token with face value, can be split or partially transferred","impact":"medium"}],"uxPatterns":[{"name":"Bond Portfolio Display","description":"Show bonds with their values and categories","userFlow":["User views bond portfolio","Bonds grouped by slot (category)","Each bond shows unique ID + value","User can split, transfer value, or redeem"]},{"name":"Split Token Flow","description":"Divide one token into two with split values","userFlow":["User selects bond to split","Enters amount for new token","UI shows resulting two tokens","User confirms split","Two bonds now exist with split values"]},{"name":"Value Transfer Between Tokens","description":"Move value from one token to another in same slot","userFlow":["User has multiple tokens in same slot","Selects source and destination","Enters value amount to transfer","Preview shows result","Value moves, token IDs preserved"]},{"name":"DeFi Position Card","description":"Display complex DeFi position as SFT","userFlow":["User views LP position (SFT)","Sees unique position ID + value (liquidity)","Range and current price shown","Can add/remove partial liquidity","Transfer value to split position"]}],"uiComponents":[{"name":"SFTCard","description":"Display semi-fungible token with ID and value","states":["normal","selected","splitting","transferring"],"props":["tokenId","slot","value","metadata","onAction"]},{"name":"SlotGroup","description":"Group tokens by their slot (category)","states":["collapsed","expanded"],"props":["slot","tokens[]","totalValue"]},{"name":"SplitInterface","description":"UI for splitting token into two","states":["editing","previewing","confirming","complete"],"props":["sourceToken","splitAmount","onSplit"]},{"name":"ValueTransferFlow","description":"Move value between same-slot tokens","states":["selecting","amount-entry","confirming","complete"],"props":["fromToken","toToken","amount","onTransfer"]},{"name":"MergeSelector","description":"Select same-slot tokens for an implementation-supported merge or value-consolidation flow","states":["selecting","ready","merging","complete"],"props":["eligibleTokens[]","selectedTokens[]","onMerge"]}],"antiPatterns":[{"pattern":"Displaying SFTs like regular NFTs (no value shown)","why":"The value IS the key differentiator, hiding it loses the point","instead":"Always show token ID AND value prominently","severity":"critical"},{"pattern":"Allowing transfers between different slots","why":"Slots represent different categories, can't mix bond types","instead":"Only show compatible (same-slot) tokens for value transfer","severity":"high"},{"pattern":"Confusing split with burn-and-mint","why":"Split preserves the original token (with less value)","instead":"Show original token continues to exist with reduced value","severity":"medium"},{"pattern":"Not grouping by slot","why":"Users can't see which tokens are compatible for operations","instead":"Group tokens by slot in portfolio view","severity":"medium"},{"pattern":"Complex slot IDs shown to users","why":"Slot is often a technical hash, meaningless to users","instead":"Map slots to human-readable categories: \"Corporate Bonds\"","severity":"medium"},{"pattern":"Not explaining what slot means","why":"Users don't understand why some tokens can interact and others can't","instead":"Explain: \"Tokens in same category can share value\"","severity":"low"}],"keyTakeaways":["ERC-3525 = NFT with a value (ID + amount)","Slot groups compatible tokens (same category)","Can split one token into two with divided value","Can transfer value between same-slot tokens","Always display both token ID and value"],"technicalNotes":"ERC-3525 defines semi-fungible tokens with token IDs, slots, and values. Value transfer uses the overloaded transferFrom(uint256 fromTokenId, uint256 toTokenId, uint256 value) or transferFrom(uint256 fromTokenId, address to, uint256 value) functions; there is no transferValueFrom selector. Approval can be full, token-level, or value-level; slot-level approval is an optional extension, so feature-detect it and show the scope being authorized. Same-slot compatibility is required for value transfers.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-3525 official specification","url":"https://ercs.ethereum.org/ERCS/erc-3525","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-3525","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-3525","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-3525","markdown":"https://www.eipsfordesigners.com/standards/ERC-3525/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-3525/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-3525","official":"https://ercs.ethereum.org/ERCS/erc-3525","discussion":"https://ethereum-magicians.org/search?q=ERC-3525"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}