id-slot-units mechanism
Core Mechanism
A core mechanism in vouchers is the data structure of the ERC-3525 token: tokenId-slot-units.
ERC-3525 inherits ERC-721’s core structure - the tokenID-metadata mechanism, and extends it with a new parameter “units” and new metadata structures to enable quantitative operations for NFTs.

tokenId-slot-units

tokenId

The tokenId of ERC-3525 is equivalent to the tokenID in ERC-721 that reflects the non-fungible aspect of digital assets. Both are defined as the same in the value type of uint 256, that is unsigned integers in the size of 256 bits in Solidity.

slot

slot is a new parameter, and its value now is implemented by hashing its underlying properties as an abstraction, with the value type of uint 256 in Solidity.
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.

units

Then there is units representing amounts for an ERC-3525 token, with the value type of uint 256 in Solidity. Each “units” attribute is comparable to the “balance” attribute in ERC-20, and transferring any amount of units to others is like transferring the balance of an ERC-20 token.
And similar to decimals() in ERC-20, the value of unitDecimals in ERC3525 also defines the decimal point of the units, with the value type of uint 8.

slot metadata

The slot metadata describes the details of the abstracted properties that are hashed as the value of the slot parameter, and values of these properties may be strings, numbers, object or arrays.
The schema of this metadata shows as follows:
{
"title": "Slot Metadata",
"type": "object",
"properties": {
"tokensInSlot": {
"type": "integer",
"description": "Number of tokens in this slot, i.e., the number of tokens that have same business attributes represented by the slot."
},
"unitsInSlot": {
"type": "integer",
"description": "Total units in the slot, since tokens in same slot are fungible, this value equals to the sum of units of all tokens in this slot."
},
"properties": {
"type": "object",
"description": "Arbitrary properties. Values may be strings, numbers, object or arrays."
}
}
}
This metadata could implement various classifications to describe complex assets for decentralized finance.
Of all classifications, the most famous one is PhyloCode for biotaxonomy. Take apples as an example, as a species, all apples could be described as the completely fungible thing as M. domestica Species, Malus Genus, Rosaceae Family, Rosales Order, Magnoliopsida Class, Magnoliophyta Phylum, Plantae Kingdom, Eukarya Domain, all other detailed differences are neglected.
But talking about a specific apple, it varies from others at harvest in size, shape, color, and acidity, etc., as non-fungible objects. These low-level details construct a new classification that people want to buy or not, especially for their pricing in the market.
With a proper classification, any real-world asset could be described as fungible in a certain category, but completely non-fungible with low-level details. This mechanism works in the real-world economy, and the slot metadata is the perfect implementation to describe this classification - tokens with different slot values are non-fungible, and tokens with the same slot value are fungible with each other (as the same category).
Then in the same category (slot), amounts (defined as units) make meanings for the underlying NFTs. One token can be split into several tokens as subtractions, with the sum of their units equal to the original units. And several tokens with the same slot can be merged together as additions, with units of the new token equal to the sum of all units of the merged tokens.
To get the specific values of properties of a slot, use the function getslot(). Take Vesting Voucher for an example, the code works as follows:
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;
}
The value of the property claimType_ represents a certain release rule that is configured for a Vesting Voucher: 0 is linear release, 1 is one-time release, and 2 is staged release.
And values of other properties like term_, maturities_, and percentages_ represent the specific configurations for the release rule of this Vesting Voucher.
The simplest configuration is one-time release. The value of its term_ is 0; the value of its maturities_ is the specific timestamp when the underlying assets of this Vesting Voucher are to be released, let’s say 2029-01-04T02:15(UTC); and the value of its percentages_ represents the specific percentage of the underlying assets to be released at the maturities_ time, in this case, the percentage is 100%.
Then is the configuration of linear release. The value of its term_ represents the vesting period for the underlying assets of this Vesting Voucher, let’s say 30 days; the value of its maturities_ is the specific timestamp when underlying assets are to be totally released, it could be calculated by the vesting period and the start time configured for this release rule, like 2028-12-05T02:15(UTC); and the value of its percentages_ also represents the percentage of the underlying assets to be totally released - 100%, and during this vesting period, these assets will be released linearly by the second (Release Volume per second = Total Volume / Release Period / 86400).
The most complex configuration for Vesting Vouchers is staged release, let’s say 4 stages with percentages of 10%, 20%, 30%, and 40% at each stage. The value of its term_ also represents the vesting period from the release time of the first stage to the release time of the final stage; then values of its maturities_ are these 4 specific timestamps for the time when underlying assets are to be released, like 2028-10-31T02:15(UTC), 2028-11-11T02:15(UTC), 2028-12-25T02:15(UTC), and 2029-01-04T02:15(UTC); and values of its percentages_ represent corresponding percentages of the underlying assets to be released at each timestamp, in this case, they are 10% at 2028-10-31T02:15(UTC), 20% at 2028-11-11T02:15(UTC), 30% at 2028-12-25T02:15(UTC), and 40% at 2029-01-04T02:15(UTC).
With different structures in slot metadata, we will create different types of vouchers, thus bringing a lot more Financial NFTs to the decentralized world.
References:
  1. 1.
    SOLV Vouchers - VNFT-core, https://github.com/solv-finance/solv-v2-voucher/blob/main/packages/solv-voucher/contracts/ICToken.sol
  2. 2.
    Semi-Fungible Token Standard, EIP-3525, https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3525.md
  3. 3.
    Transgressing the Boundaries Of DeFi: Interview With Co-Founder Of Solv Protocol By Edward Moon | Analysis From Moon | 9 Dec 2021, https://www.publish0x.com/analysis-from-moon/transgressing-the-boundaries-of-defi-interview-with-co-found-xxypzwd