Standards

ERC3475 Abstract Storage Bonds

A guide to ERC3475 tokenized storage bonds, supporting complex debt structures, redemption logic and scalable on-chain fixed-income assets.

Last updated: 12/4/2025
Improve this page

ERC3475, referred to as Abstract Storage Bonds, is a token standard designed to represent tokenized debt obligations such as bonds, notes, repayment claims, and revenue-sharing instruments. Instead of using a single fungible or non-fungible format, the standard defines two identifying layers: classId to distinguish bond categories or series, and nonceId to define specific issuance terms like maturity windows, coupons, tranches, or redemption conditions. Each (classId, nonceId) pair behaves like a semi-fungible slice of a broader debt instrument, allowing issuers to customize repayment logic, timelines, and metadata without deploying new contracts for each issuance.

Because ERC3475 is built for capital-market scale usage, it includes batch operations for issuing, redeeming, transferring, and approving positions, which reduces gas costs and improves back-office integrations. Its event architecture makes it easy for indexers, custodians, lending desks, and compliance solutions to track maturity, redemption progress, and settlement states on-chain.

The standard was created to solve a core limitation of ERC20, ERC721, and ERC1155: none of them support obligation-specific metadata or structured lifecycle tracking out of the box. ERC3475 provides a unified representation for bonds that can evolve over time, enabling transparent secondary markets, pool-based liquidity, and automated redemption flows. Finalized in 2021, it does not depend on a base token interface but is easily detected via events and ERC165. In real-world asset systems, it is especially useful for tokenized fixed-income products such as treasuries, private credit, and commercial paper, often combined with ERC3643 to encode compliance requirements or ERC4626 vaults to streamline yield distribution.
 

Use of ERC3475 in RWA Contexts

ERC3475 enables real-world debt instruments to be expressed as programmable on-chain bonds. Each asset can be represented through a combination of classId and nonceId, where classes define risk tiers or tranche types and nonces represent specific issuance cycles, maturity windows, or coupon structures. This approach allows issuers to tokenize complex debt positions while tracking progress, redemptions, and repayment schedules directly on-chain through interfaces like getProgress. Batch execution makes the standard especially suitable for institutional RWA flows, where hundreds or thousands of positions must settle in a single transaction.
 

Key Applications in RWA Development

Tokenized Fixed-Income Products Private credit lines, treasury exposures, and commercial loans can be issued as distinct class and nonce combinations. Issuers can mint allocations to lenders, and later use redemption calls to trigger payouts at maturity or coupon checkpoints. Batch transfers allow compliant secondary trading without requiring multiple contract deployments.

Structured Revenue Instruments For assets like income-generating real estate or renewable energy credits, classes can represent share tiers such as senior and junior claims. Redemptions and progress states reflect interest payments or revenue shares over time, while off-chain valuation or rental data feeds can update redemption metadata.

Compliance-Aware Issuance Metadata fields can embed regulatory detail, credit score ranges, jurisdiction, or investor eligibility rules. When combined with ERC3643, transfers and approvals can remain gated to qualified or licensed participants, making ERC3475 suitable for ABS-style structures and regulated RWA credit issuance.

Liquidity Pool Integration Class-level liquidity aggregation allows AMMs to pool multiple issuance rounds within a shared curve while keeping their repayment schedules distinct. This prevents fragmentation across vintages while enabling accurate LP accounting using activeSupply or redemption-adjusted totals.

Developer Advantages in RWA Builds

Efficient Multi-Asset Operations Native batch formats cut operational gas usage significantly and remove the need for looping logic when settling or reallocating large portfolios, making it far easier to handle institutional-grade bond books or monthly repayment cycles.

Metadata On-Chain by Design Value structures support dynamic numerical, textual, and address-based fields without relying on IPFS, which simplifies audits and provides transparent repayment history both for investors and regulators.

Plug-and-Play With Existing Ecosystems Because events mirror familiar ERC token flows, wallets and indexers can support ERC3475 without heavy modification. DeFi protocols can rely on total, circulating, or redeemed supply variants to price tranches accurately.

Architecture Flexibility Over Time Issuance logic can live in dedicated management contracts, allowing upgrades or standard extensions without touching existing debt records. This is especially useful when bond terms roll, convert, or refinance without resetting full on-chain mappings.

The following sequence diagram illustrates issuing and redeeming an RWA bond (e.g., tokenized treasury), from mint to batch redemption. It underscores batch ops and progress tracking for developer flows.

Abstract storage.svg

Core Specification

ERC3475 contracts are required to implement the core interface using batch-capable functions built around the Transaction struct. Supply accounting is maintained at the granularity of each (classId, nonceId) pair, with separate tracking for total, redeemed, active, and burned amounts. Transfers rely on an allowance model similar to ERC20, while metadata and value fields are stored in indexed arrays, allowing issuers to attach flexible data without restructuring the contract. View functions are expected to return zero values rather than revert when an invalid identifier is queried, ensuring predictable behavior for indexers and secondary market tools.

Key definitions

  • ClassId A uint256 identifier representing a bond category or debt tranche, such as senior, mezzanine, or junior claims.
  • NonceId A uint256 identifier distinguishing specific issuance conditions within a class. Each nonce maps to its own maturity terms, coupon rules, or redemption window.
  • Transaction A struct defined as {uint256 classId; uint256 nonceId; uint256 _amount}, used to bundle multiple actions for efficient issuance, transfer, and redemption.
  • Metadata A struct containing {string title; string _type; string description} that defines core descriptive attributes for bond discovery, regulatory context, or instrument classification.
  • Values A flexible struct {string stringValue; uint256 uintValue; address addressValue; bool boolValue; bytes bytesValue} that allows on-chain tracking of changing parameters such as NAV marks, oracle-supplied interest accruals, legal tags, or settlement instructions.
  • Progress A redemption status indicator represented as (achieved, remaining) that signals how much of a bond’s commitment, interest, principal, or streamable yield, has been fulfilled versus what remains outstanding.

The standard defines IERC3475. Below is the full ABI-compliant interface from the EIP.

1// SPDX-License-Identifier: CC0-1.0
2pragma solidity ^0.8.0;
3
4interface IERC3475 {
5    struct Transaction {
6        uint256 classId;
7        uint256 nonceId;
8        uint256 _amount;
9    }
10
11    struct Metadata {
12        string title;
13        string _type;
14        string description;
15    }
16
17    struct Values {
18        string stringValue;
19        uint256 uintValue;
20        address addressValue;
21        bool boolValue;
22        bytes bytesValue;
23    }
24
25    // Events
26    event Issue(address indexed _operator, address indexed _to, Transaction[] _transactions);
27    event Redeem(address indexed _operator, address indexed _from, Transaction[] _transactions);
28    event Burn(address _operator, address _owner, Transaction[] _transactions);
29    event Transfer(address indexed _operator, address indexed _from, address indexed _to, Transaction[] _transactions);
30    event ApprovalFor(address indexed _owner, address indexed _operator, bool _approved);
31
32    // Core Operations
33    function transferFrom(address _from, address _to, Transaction[] calldata _transactions) external;
34    function transferAllowanceFrom(address _from, address _to, Transaction[] calldata _transactions) external;
35    function issue(address _to, Transaction[] calldata _transactions) external;
36    function redeem(address _from, Transaction[] calldata _transactions) external;
37    function burn(address _from, Transaction[] calldata _transactions) external;
38    function approve(address _spender, Transaction[] calldata _transactions) external;
39    function setApprovalFor(address _operator, bool _approved) external returns (bool);
40
41    // Supply Queries
42    function totalSupply(uint256 classId, uint256 nonceId) external view returns (uint256);
43    function redeemedSupply(uint256 classId, uint256 nonceId) external view returns (uint256);
44    function activeSupply(uint256 classId, uint256 nonceId) external view returns (uint256);
45    function burnedSupply(uint256 classId, uint256 nonceId) external view returns (uint256);
46
47    // Balance & Allowance
48    function balanceOf(address _account, uint256 classId, uint256 nonceId) external view returns (uint256);
49    function allowance(address _owner, address _spender, uint256 classId, uint256 nonceId) external view returns (uint256);  // Note: view in impl
50    function isApprovedFor(address _owner, address _operator) external view returns (bool);
51
52    // Metadata & Values
53    function classMetadata(uint256 metadataId) external view returns (Metadata memory);
54    function nonceMetadata(uint256 classId, uint256 metadataId) external view returns (Metadata memory);
55    function classValues(uint256 classId, uint256 metadataId) external view returns (Values memory);
56    function nonceValues(uint256 classId, uint256 nonceId, uint256 metadataId) external view returns (Values memory);
57
58    // Progress
59    function getProgress(uint256 classId, uint256 nonceId) external view returns (uint256 progressAchieved, uint256 progressRemaining);
60}

Detailed Function Breakdown

transferFrom(address _from, address _to, Transaction[] calldata _transactions) Moves multiple bond positions in a single call. Balances and allowances are reduced according to each transaction item, and a Transfer event is emitted. In RWA trading environments, this is commonly used for bulk settlement of debt slices across secondary venues. The call reverts if the sender does not hold enough active (non-redeemed, non-burned) supply.

issue(address _to, Transaction[] calldata _transactions) Creates new bond units and assigns them to the specified address. totalSupply is increased per (classId, nonceId) and an Issue event is emitted. In institutional RWA flows, issuance rights are typically admin-restricted, enabling banks, credit desks, or note trustees to mint regulated instruments such as treasuries or private debt tranches.

redeem(address _from, Transaction[] calldata _transactions) Reduces the holder’s outstanding balance to reflect repayment or maturity events, increasing redeemedSupply. A Redeem event signals the update. For RWAs, redemption corresponds to principal or coupon settlement. Implementations often verify full maturity progress using getProgress.

burn(address _from, Transaction[] calldata _transactions) Permanently destroys selected positions and increments burnedSupply. A Burn event records the action. This is used for early closures, workout resolutions, covenant breaches, write-downs, or cancelled issuance blocks.

approve(address _spender, Transaction[] calldata _transactions) Assigns granular allowances for specific bond batches defined by (classId, nonceId). Institutional desks use this to authorize brokers or custodial systems to move only targeted debt sets rather than the entire portfolio.

setApprovalFor(address _operator, bool _approved) Grants or removes blanket transfer authority for all positions, emitting ApprovalFor. In RWA contexts, this pattern supports full delegation to fund administrators or custodians, but due to its scope it is restricted in most compliance environments.

totalSupply(uint256 classId, uint256 nonceId) Returns the total minted quantity associated with the specified debt tranche and issuance round. Front ends and AMMs use this for AUM snapshots, alongside related supply views tracking redeemed or active units.

getProgress(uint256 classId, uint256 nonceId) Reports a tuple reflecting settlement status: how much has been repaid versus what remains. RWA use cases often feed this value from repayment or yield oracles to align on-chain state with real-world amortization schedules.

classMetadata(uint256 metadataId) Returns descriptive attributes tied to a bond class, such as its name, tranche level, or overview text. Market interfaces use this to visually label instruments like “Series A Senior Secured” or “Green Credit Tier 2.”

nonceValues(uint256 classId, uint256 nonceId, uint256 metadataId) Retrieves dynamically stored issuance data for that class–nonce combination, such as coupon percentage, maturity date, rating tag, or underlying asset reference. For RWAs, these fields allow live updates without redeploying contracts, enabling compliant reflection of interest rate resets, valuation feeds, or disclosure amendments.

Reference Implementation

The EIP provides a Solidity implementation: ERC3475.sol. It uses mappings for supplies/balances (mapping(uint256 => mapping(uint256 => uint256)) public totalSupply;), arrays for metadata/values, and internal _transfer for batches. Includes Ownable for bank control and view helpers.

1// Excerpt from ERC3475.sol
2contract ERC3475 is Ownable {
3    // Mappings for supplies, balances, allowances
4    mapping(uint256 => mapping(uint256 => uint256)) public override totalSupply;
5    mapping(address => mapping(uint256 => mapping(uint256 => uint256))) public override balanceOf;
6    // Metadata arrays: classMetadata[metadataId], etc.
7    Metadata[] public classMetadata;
8    // Constructor: Ownable(msg.sender); init arrays if needed
9    constructor() Ownable(msg.sender) {}
10}

This deployment pattern places the issuer or trustee (the contract owner) at the center of bond creation and redemption. In real RWA scenarios, metadata for each bond class, such as tranche title, regulatory category, or coupon profile, is typically populated immediately after deployment rather than at constructor time.

Key Functions

issue

1function issue(address _to, Transaction[] calldata _transactions) external override onlyOwner {
2    for (uint i = 0; i < _transactions.length; i++) {
3        Transaction memory tx = _transactions[i];
4        totalSupply[tx.classId][tx.nonceId] += tx._amount;
5        balanceOf[_to][tx.classId][tx.nonceId] += tx._amount;
6    }
7    emit Issue(msg.sender, _to, _transactions);
8}

This function mints new debt units to the target account and updates supply counts for each (classId, nonceId) being issued. In institutional RWA issuance, this logic is often extended with oracle-fed caps, underwriting checks, or subscription allocations for regulated tranches.

redeem

1function redeem(address _from, Transaction[] calldata _transactions) external override onlyOwner {
2    for (uint i = 0; i < _transactions.length; i++) {
3        Transaction memory tx = _transactions[i];
4        require(balanceOf[_from][tx.classId][tx.nonceId] >= tx._amount, "Insufficient balance");
5        balanceOf[_from][tx.classId][tx.nonceId] -= tx._amount;
6        redeemedSupply[tx.classId][tx.nonceId] += tx._amount;
7    }
8    emit Redeem(msg.sender, _from, _transactions);
9}

Redemption decreases the holder’s position and increases the redeemed tally, preparing for payout or maturity settlement. With RWAs, this commonly includes a pre-check that getProgress indicates maturity or coupon eligibility before burning or adjusting balances.

getProgress

1function getProgress(uint256 classId, uint256 nonceId) external view override returns (uint256, uint256) {
2    // Impl: Oracle query or internal calc
3    return (activeSupply[classId][nonceId], totalSupply[classId][nonceId] - activeSupply[classId][nonceId]);
4}

This view returns how much of a given issuance remains active and how much has already been redeemed or settled. For real-world instruments, the calculation is often augmented with oracle-sensitive inputs, such as amortization curves, variable rate resets, covenant triggers, or NAV-based updates to reflect the evolving repayment profile.
 

Security Considerations

Because ERC3475 manages debt instruments with legal and financial implications, its batch mechanics and issuance authority introduce risks beyond typical token standards. The ability to mint, redeem, and approve in large sets creates meaningful attack surfaces if role control or supply math is not tightly governed. Audits for RWA implementations typically focus on invariant enforcement, event integrity, and issuance discipline.

Over-Approval Exposure

Global approvals granted through setApprovalFor can unintentionally allow operators to move positions across all classes and nonces. Mitigation includes prioritizing granular approve calls for specific transactions, setting expiration windows on delegated rights, and role-gating operator access when working with regulated RWA custodians.

Batch Reentrancy Through Loops

Because transfers, redemptions, and issuance steps are performed in arrays, a malicious contract could trigger nested calls mid-loop if hooks are added. Mitigation includes standard CEI ordering, lightweight guard rails like ReentrancyGuard, and size limits on batch payloads to prevent gas-based lockups or recursive execution.

Supply Inflation and Redemption Drift

If issuance and redemption are not strictly tied to accounting guides, totalSupplyredeemedSupply, and activeSupply can fall out of sync. Oracle-fed updates to repayment status or maturity could also be exploited to force premature redemption claims. Mitigation includes strict invariants such as total = active + redeemed + burned, multisig or role-controlled issuance, and well-monitored oracles for coupon or amortization feeds.

Metadata Mutability Risks

Metadata that defines tranche terms, coupon language, or regulatory context must not be silently altered after issuance. Post-launch edits can misstate risk tiers or maturity and lead to mispriced trades. Mitigation includes making metadata immutable after creation, using events for historical versioning, and hashing metadata to avoid silent overwrites.

Nonce Conflicts and Issuance Collisions

Reusing an existing (classId, nonceId) pair unintentionally merges supply from different issuance rounds, corrupting progress tracking and maturity schedules. Mitigation includes enforcing unique nonce generation, using hash-based IDs, and preventing reallocation in management contracts.

ERC3643 Token for Regulated EXchanges
ERC7943 Universal Real World Asset Interface

WE SECURE EVERYTHING YOU BUILD.

From day-zero risk mapping to exchange-ready audits — QuillAudits helps projects grow with confidence. Smart contracts, dApps, infrastructure, compliance — secured end-to-end.

DeFi SecurityplumeUniswap FoundationAethiropt-collectivePolygon SPNBNB Chain Kickstart

Office 104/105 Level 1, Emaar Square, Building 4 Sheikh Mohammed Bin Rashid Boulevard Downtown Dubai, United Arab Emirates P.O box: 416654

[email protected]

All Rights Reserved. © 2026. QuillAudits - LLC

Privacy Policy