Final ERC standard for semi-fungible tokens that combines ERC-721 unique IDs with an ERC-20-style quantitative `value` and a `slot` attribute. Tokens sharing the same slot are fungible by value, enabling unified representation of bonds, vesting schedules, and structured financial instruments.
- 01tokenized bonds and vesting
- 02structured financial instruments
- 03fractional NFT-like assets
- 04RWA primitives
- 05tokens with quantitative + identity dimensions
- pnpm add @solvprotocol/erc-3525
- # reference impl: https://github.com/solv-finance/erc-3525
Implement ERC-3525 by inheriting Solv Protocol's reference `ERC3525` contract (which itself extends ERC-721 + ERC-165). Each token carries a `<tokenId, slot, value>` triple: `slotOf(uint256 tokenId) returns (uint256)`, `balanceOf(uint256 tokenId) returns (uint256)` (overloaded — returns the token's `value`, distinct from ERC-721 `balanceOf(address)`), and `transferFrom(uint256 fromTokenId, address to, uint256 value)` / `transferFrom(uint256 fromTokenId, uint256 toTokenId, uint256 value)` for value transfers between tokens of the same slot. Emit `TransferValue`, `ApprovalValue`, and `SlotChanged` events. Use `valueDecimals()` (returns uint8) to expose value precision the way ERC-20 `decimals()` does. Tokens in different slots are non-fungible with each other; tokens in the same slot can split/merge value freely.
- ⚑`balanceOf` is OVERLOADED — `balanceOf(address)` returns NFT count (ERC-721 semantics) while `balanceOf(uint256 tokenId)` returns the token's `value`. Integrators that auto-resolve overloads (ethers v5 vs v6, viem) often pick the wrong one and read garbage.
- ⚑Two tokens with different `slot` values are NOT interchangeable — value transfers across slots MUST revert. Validate slot equality on every value-level transfer or you'll silently break accounting.
- ⚑Splitting via `transferFrom(fromTokenId, toAddress, value)` mints a NEW tokenId on the receiver — wallets that subscribe to ERC-721 `Transfer` events will see unexpected mints; index `SlotChanged` and `TransferValue` separately.
- ⚑Approval is two-tier: ERC-721 token-level approvals AND a separate value-level `approve(uint256 tokenId, address operator, uint256 value)`. Forgetting the value-level approve is the #1 integration bug.
- ⚑Most NFT marketplaces (OpenSea, Blur) treat ERC-3525 as plain ERC-721 and ignore `value` — listings will trade the entire `value` payload of a tokenId, which is rarely what users expect.