SFT Whitepaper 04 |Chapter 3:ERC-3525: Semi-Fungible Token Standard

The ERC-3525 Semi-Fungible Token (SFT) standard , a general-purpose standard for advanced financial instruments, has the potential to solve problems too costly to solve with ERC-721s or 1155s.

Meng Yan
14 min readDec 19, 2022

As discussed in the previous chapters, simple financial NFT standards like ERC-721 and 1155 tokens aren’t suitable for designing and creating flexible, tailor-made Web3 financial assets. The ERC-3525 Semi-Fungible Token (SFT) standard , a general-purpose standard for advanced financial instruments, has the potential to solve problems too costly to solve with ERC-721s or 1155s. This chapter explores the ERC-3525 SFT standard and its inner workings.

3.1 Transcending Financial Instruments

In a broad sense, financial instruments are monetary contracts between parties. All financial instruments are on some level financial contracts, and the easiest way for someone to enter into or get out of a financial contract is by purchasing or selling a financial instrument. The traditional finance sector (TradFi) is home to diverse financial instruments including bonds, commercial papers, options contracts, futures contracts, repurchase agreements (repos), swaps, ETFs, and ABS. These instruments (shown in figure 3.1) have a few things in common: they are fairly standardized, easy to use, and to a great extent, liquid.

Figure 3.1 Types of financial instruments in TradFi (Source: Wikipedia)

The key premise of Web3 is to transform the financial instruments from TradFi into advanced, blockchain digital assets. Most currencies, stock shares, and point systems can be represented by ERC-20 tokens, but this is only the first leg of TradFi. As for financial instruments, the second leg of TradFi, there are no successful parallels before ERC-3525.

When it comes to defining the candidate asset for financial instruments, we need to consider several criteria. This includes the ability to send or receive payments directly from the token, which increases the efficiency of delivering a contract, and visualization for the ease of use and the look-through transparency. But fundamentally, we think that such an asset must have two overarching characteristics in order to represent most, if not all, financial instruments:

  • Fungible (can be fractionalized). A user should be able to split up a token denominated in $100 USD into two $50 tokens or four $25 tokens, etc. This same applies to combining tokens as well.
  • Transitive. Fractionalizing and combining tokens should not alter the token type but only the value tied to the tokens.

The ERC-3525 Semi-Fungible Token is the only token standard to fit the bill and thus an ideal vehicle for Web3 digital assets.

The rest of this chapter provides a technical overview of the ERC-3525 Semi-Fungible Token standard. Feel free to skip to Chapter 4: Semi-Fungible Tokens in Use if you wish to learn about the real-world use cases of ERC-3525 tokens.

3.2 An Overview on Token Technology

The Ethereum whitepaper describes a currency, or a token system, as “a database with one operation.” A token or tokenized asset, in other words, is a certain type of data that can be operated upon.

This perspective provides an easy way to understand an ERC-20 token: their core data structure is a 256-bit unsigned integer defined by _value or balanceOf(), and a decimal function, decimals(), will determine the token amount. Transferring tokens by this amount, which is implemented through transfer(), transferFrom(), and approve(), is the main operation for sending tokens from one address to another.

Unlike ERC-20s, ERC-721s abandoned the token amount in their data structure. As a result, the 256-bit unsigned integer that originally designates the token amount now defines a unique _tokenID which represents the ownership over digital or physical assets. Although NFTs share the same address-to-address

operation as ERC-20s, the ERC-20 transfer is quantitatively operated on the token amount, whereas the ERC-721 transfer is operated qualitatively on the _tokenID and the amount will always be “1,” to simulate the transfer of ownership, implemented by transferFrom(), safeTransferFrom(), and approve(), etc.

Another noteworthy feature of ERC-721 is its metadata extension. With properties like name, description, and image, ERC-721 NFTs could for the first time in history display rich visual information on the blockchain. For example, in the metadata extension for Uniswap’s V3 LP token, custom price curve and liquidity position are presented in an aesthetically pleasing way. On the other side of the coin, however, since ERC-721 can’t be fractionalized, the liquidity for V3’s positions is still largely limited.

A possible solution is the combination of the _value-based quantitative attributes of ERC-20s and the _tokenID-based qualitative data structures of ERC-721s. It may lead us to a happy semi-fungible middle between the fungible and the non-fungible.

ERC-1155 token standard was among the early efforts to explore the concept of SFT. In a nutshell, the ERC-1155 implements multiple token types on a single contract, and it does so by using an _id attribute in its data structure to represent a configurable token type and _value, a quantitative attribute, to represent the transfer amount of that token type. For the deployment of multiple token types, ERC-1155 also creates a new transfer model, the Batch Transfer safeBatchTransferFrom(), where transferring multiple types of tokens in different amounts from one group of users (addresses) to another is made possible.

It should be noted here that there’s no decimal-like concept in ERC-1155, so it isn’t possible to implement more precise data for financial applications. And the issue with using _id to represent token types is that tokens under the same _id are completely fungible. _id is a classifying function rather than signifying the non-fungible quality, like _tokenID does. As a result, ERC-1155s are efficient in scenarios involving multi-tokens like virtual in-game items but won’t be compatible with ERC-721s, nor are they scalable in non-game applications.

A more practical schema for the SFT would be to bring in the _value-based, quantitative operations while keeping the _tokenID-based non-fungible aspects, with additional innovation on a more generalized classification mechanism like _slot for financial scenarios or business purposes. This new schema is ERC-3525. With ERC-3525, we can enable more in-depth financial applications of SFTs by leveraging an organic balance between the fungible and the non-fungible.

Figure 3.2 Semi-Fungible Token Standard

The ERC-3525 token standard inherits ERC-721’s core structure: _tokenID and metadata extension. It also adopts the standard quantitative attribute _value and extends it with a categorizing attribute _slot (along with Slot Metadata to implement the categorizing function of _slot.) The new data structure within the ERC-3525 is defined as an <ID, SLOT, VALUE> Triple Scalar Model.

One of ERC-3525’s major innovations on token operations is token(_tokenId)-to-token(_tokenId) transfer, or quantitative operations for NFTs, implemented by function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable and function safeTransferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external payable. This means that ERC-3525 tokens sharing the same _slot are fungible to each other and that one ERC-3525 token(_tokenId) may be transferred to another. The feature marks the first time in history that an Ethereum token has gained equal status as users (externally owned accounts, or EOA) or smart contracts (contract accounts).

Furthermore, ERC-3525 has inherited the traditional address-to-address transfer within the ERC-721 standard, which is implemented by transferFrom(), safeTransferFrom().

Figure 3.3 shows a comparison among ERC-20, ERC-721, ERC-1155, and ERC-3525 tokens in terms we’ve discussed so far.

Figure 3.3 A comparison of token standards: ERC-20, ERC-721, ERC-1155 & ERC-3525

Furthermore, ERC-3525’s token data is a two-layer structure that is an upgrade of one-layer data structures found within ERC-20, ERC-721 and ERC-1155 tokens. The _value in ERC-20, _tokenID in ERC-721, and (_id, _value) in ERC-1155 are owned by an address. While the _tokenId in ERC-3525 is owned also by an address, the _value is contained or “owned” by the _tokenId.

Figure 3.4 The two-layer structure of ERC-3525’s token data

3.3 ERC-3525 Core Mechanisms

ERC-3525’s greatest innovation is the <ID, SLOT, VALUE> Triple Scalar Model. _slot/Slot Metadata enables quantitative operations for NFTs through the utilization of standard balanceOf() / _value mechanism.

tokenId

The _tokenId in ERC-3525 is defined as a value type in terms of a uint 256, an unsigned integer in the size of 256 bits in Solidity, and is equivalent to the _tokenID in ERC-721. _tokenId reflects the non-fungible aspect of digital assets.

slot

_slot is a new attribute that categorizes variables in financial or business applications. One may implement _slot by hashing its underlying properties as a uint-256 abstraction.

A critical tip for implementation here is the uniqueness of the _slot value. As long as different slots have different values, there could be different solutions (such as uint 8).

value

Similar to the balanceOf() / _value mechanism in ERC-20, balanceOf() in ERC-3525 can be used to query the amount of the underlying asset of an ERC-3525 token, and its value type is defined as a Solidity-based uint 256. That means transferring ERC-3525’s _value to other addresses works similarly to transferring _value for ERC-20 tokens.

3.3.1 Token Operations

The address-to-address transfer in ERC-3525 is compatible with that of ERC-721. Please refer to the code sample in 2.1.1 if needed.

What makes ERC-3525 special is its ability to transfer only a fraction of the underlying asset within an ERC-3525 token. Such a transfer is implemented through the token(_tokenId)-to-token(_tokenId) transfer function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable; and the token(_tokenId)-to-address transfer function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable;.

As mentioned previously, the token(_tokenId)-to-token(_tokenId) transfer applies to the transferring of ERC-3525 tokens that have the same _slot. This type of transfer allows users to transfer an amount of the underlying asset within an ERC-3525 token(_tokenId) to another:

/**
* @notice Transfer value from a specified token to another specified token with the same slot.
* @dev Caller MUST be the current owner, an authorized operator or an operator who has been
* approved the whole `_fromTokenId` or part of it.
* MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist.
* MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match.
* MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the
* operator.
* MUST emit `TransferValue` event.
* @param _fromTokenId The token to transfer value from
* @param _toTokenId The token to transfer value to
* @param _value The transferred value
*/
function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable;

The token(_tokenId)-to-address transfer applies to the transferring of a certain amount of underlying asset within an ERC-3525 token (_tokenId) to a different address. The token(_tokenId)-to-address transfer allows a new ERC-3525 token with the same _slot value as the original token to receive the asset transferred:

/**
* @notice Transfer value from a specified token to an address. The caller should confirm that
* `_to` is capable of receiving ERC-3525 tokens.
* @dev This function MUST create a new ERC-3525 token with the same slot for `_to` to receive
* the transferred value.
* MUST revert if `_fromTokenId` is zero token id or does not exist.
* MUST revert if `_to` is zero address.
* MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the
* operator.
* MUST emit `Transfer` and `TransferValue` events.
* @param _fromTokenId The token to transfer value from
* @param _to The address to transfer value to
* @param _value The transferred value
* @return ID of the new token created for `_to`, which receives the transferred value
*/
function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable returns (uint256);

3.3.2 “Slot” Metadata

“Slot” metadata describes the details of the abstracted properties that are hashed as the value of the _slot attribute. The value of these properties may be strings, numbers, objects, or arrays. Slot metadata can implement various classifications to describe complex DeFi assets. Here’s the JSON schema of slot metadata:

{
"title": "Token Metadata",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Identifies the asset to which this NFT represents"
},
"description": {
"type": "string",
"description": "Describes the asset to which this NFT represents"
},
"image": {
"type": "string",
"description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents."
},
"balance": {
"type": "BigNumber",
"description": ""
},
"slot": {
"type": "BigNumber",
"description": "The id of the slot that this token belongs to."
},
"properties": {
"type": "object",
"description": "Arbitrary properties. Values may be strings, numbers, objects or arrays."
}
}
}

To further illustrate the concept of semi-fungible-ness, let’s think for a second about the PhyloCode, a rule system governing the naming of taxa in biology. Take apples, for example. Under the phylogenetic classifications such as M. domestica species, malus genus, Rosaceae Family, or Eukarya Domain, all apples could be treated as indistinguishable or “fungible.” Inspecting individual apples, however, we start to find discrepancies in secondary properties like size, shape, color, acidity, etc. It’s these qualities that make apples non-fungible. PhyloCode may deem these qualities negligible for scientific taxonomy, but it’s exactly these qualities that dictate most, if not all, of our day-to-day shopping decisions. RWAs (real-world assets) are fungible in a given category (e.g., real estate property) but non-fungible when taking into account certain secondary qualities (e.g. location).

Returning to ERC-3525, slot metadata enables secondary-quality nuances so that tokens with different _slots are non-fungible, and those with the same _slot value are fungible. The slot metadata is what gives the token fluidity in fungibility.

_slot also affects how _value works: All the NFTs fractionalized from the original NFT can be merged through simple arithmetic addition (or subtraction, if splitting) of their _values.

What _slot actually represents in a financial application is arbitrarily defined. In a Vesting Voucher (an SFT that locks and vests tokens based on a predetermined schedule), different vesting schedules are assigned their corresponding _slot through the getslot() function:

function getSlot(
uint8 claimType_,
uint64[] memory maturities_,
uint32[] memory percentages_,
uint64 term_
) internal pure virtual returns (uint256) {
uint256 first = uint256(
keccak256(
abi.encodePacked(
claimType_,
term_,
maturities_[0],
percentages_[0]
)
)
);
if (maturities_.length == 1) {
return first;
}
uint256 second;
for (uint256 i = 1; i < maturities_.length; i++) {
second = uint256(
keccak256(
abi.encodePacked(second, maturities_[i], percentages_[i])
)
);
}
return uint256(keccak256(abi.encodePacked(first, second)));
}

In this code, claimType_ represents a certain release rule configuration in the Vesting Voucher: 0 (linear release), 1 (one-time release), and 2 (staged/custom release). Take the linear release, for example. Term_ represents the vesting period (from the day the vesting starts to the fully vested date) for the Voucher’s underlying asset in days. Maturities_ marks the fully vested date, which is calculated based on the vesting period (e.g., 02:15 on December 5, 2028, UTC). Percentages_ represents the percentage of remaining, unvested underlying assets. (“100%” in percentages_ means the tokens are fully vested for a given vesting period.)

Slot metadata is a flexibility-centric parameter that will shape an SFT-based FI and the purposes it will serve.

3.4 A Paradigm Shift in Asset Transfer

ERC-3525’s token(_tokenId)-to-token(_tokenId) transfer transforms the way blockchain-based assets are transferred. To understand why it is important, let’s look at an example based on the use of bitcoin.

Imagine Alice and Bob each have $50 in bitcoin in their accounts. After Alice sends $20 to Bob, her remaining balance is $30, and Bob now has $70. Through Bitcoin’s state transition function, the transfer process looks something like this:

APPLY({ Alice: $50, Bob: $50 }, "send $20 from Alice to Bob") = { Alice: $30, Bob: $70 }

With the advent of smart contracts, the original account system utilized in Bitcoin has been expanded into EOA (that are controlled by private keys) and contract accounts (that are controlled by their contract codes). An EOA has no code and can allow anyone to send messages by creating and signing a transaction through itself. A contract account, on the other hand, can read and write to internal storage, send a new message, or even create a new contract when it receives a message from outside itself.

Ethereum’s transfer function looks something like this:

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)

​​Since Ethereum has no restriction as to what type of account can transact with the other and what can’t, it has built an entire ecosystem supported by asset transfers among EOAs and contract accounts.

ERC-3525 provides a paradigm shift in the asset transfer model, namely, token(_tokenId)-to-token(_tokenId) transfer. It enables receiving, storing, and transferring of an asset from tokens, rather than addresses:

function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable

In ERC-3525, a token(_tokenId)-to-address transfer involves creating and transferring to the recipient’s address a new SFT with the same _slot as the original. An address-to-token(_tokenId) transfer would involve custom programming based on the Ethereum Transaction Simulation.

Figure 3.5 ERC-3525’s token(id)-to-token(id) transfer

To sum it up, ERC-3525 SFTs have gained equal importance as EOAs and contract accounts. Like Ethereum smart contracts, ERC-3525 SFTs are capable of external interactions, conditional judgments, and smart executions — the whole nine yards.

3.5 Smart Contract Visualization

ERC-3525 also utilizes metadata extensions which enhance the ways strings, numbers, objects or arrays, or images of ERC-3525 tokens are visually delivered. ERC-3525’s metadata’s JSON schema is as follows:

{
"title": "Token Metadata",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Identifies the asset to which this token represents"
},
"decimals": {
"type": "integer",
"description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation."
},
"description": {
"type": "string",
"description": "Describes the asset to which this token represents"
},
"image": {
"type": "string",
"description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
},
"properties": {
"type": "object",
"description": "Arbitrary properties. Values may be strings, numbers, objects, or arrays."
}
}
}

The text-rich description helps provide sophisticated information about a token, and the information-rich image in turn enhances the token’s text description.

Slot metadata not only provides the details of the abstracted properties that are hashed into slot but also serves as the foundation of quantitative operations for SFTs, like splitting and merging.

Contract Metadata describes the smart contract in terms of name, text description, unitDecimals, etc. Its JSON schema is as follows:

{
"title": "Contract Metadata",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Contract Name"
},
"description": {
"type": "string",
"description": "Describes the contract "
},
"unitDecimals": {
"type": "integer",
"description": "The number of decimal places that the units should display - e.g. 18, means to divide the token units by 1000000000000000000 to get its user representation."
},
"properties": {
"type": "object",
"description": "Arbitrary properties. Values may be strings, numbers, object or arrays."
}
}
}

3.6 The Roadmap

October 2020: Inception of an ERC-721 compatible token standard in response to the absence of sophisticated financial instruments in DeFi

December 1, 2020: Created ERC-3525 proposal

April 2021: The first field-tested SFT product: Vesting Voucher (introduced by Solv Protocol)

July 2021: Submission of the first version of ERC-3525

October 2021: “Draft” stage

April 2022: Added token-to-token transfer

September 5, 2022: Final ERC

To learn more about ERC-3525, please visit Ethereum’s GitHub repository at https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3525.md.

To learn more about the EIP process, please visit https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md.

SFT Whitepaper : https://sftlabs.io/

ERC-3525 reference implementation v1.1.0:https://www.npmjs.com/package/@solvprotocol/erc-3525

A telegram group for EIP-3525 builders: https://t.me/EIP3525_DEV

--

--

Meng Yan

Co-founder of Solv Protocol, Co-author of ERC-3525 standard