Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion for EIP-3525: The Semi-fungible Token Standard #5183

Closed
Will42W opened this issue Jun 27, 2022 · 8 comments
Closed

Discussion for EIP-3525: The Semi-fungible Token Standard #5183

Will42W opened this issue Jun 27, 2022 · 8 comments

Comments

@Will42W
Copy link
Contributor

Will42W commented Jun 27, 2022

eip: 3525
title: Semi-Fungible Token Standard
author: Will Wang (@will-edge), Mike Meng myan@solv.finance, Ethan Y. Tsai yee.tsai@gmail.com, Ryan Chow ryanchow@solv.finance, Zhongxin Wu wuzhongxin@solv.finance, AlvisDu alvis@solv.finance
discussions-to: #5183
status: Draft
type: Standards Track
category: ERC
created: 2020-12-01
requires: 165, 721

Table of Contents

Simple Summary

This is a standard for semi-fungible tokens. The set of smart contract interfaces described in this document defines an ERC-721 extension/compatible token standard. This standard introduces an <ID, SLOT, VALUE> triple scalar model that represents the semi-fungible structure of a token. It also introduces new transfer models as well as approval models that reflect the semi-fungible nature of the tokens.

Abstract

ERC-3525 is an ERC-721 compatible token standard, where each ERC-3525 token, like an ER-721 token, contains an ID property to identify itself as a universally unique entity, so that the tokens can be transferred between addresses and approved to be operated in ERC-721 compatible way.

ERC-3525 tokens also contain an 'value' property, representing the quantitative nature of the token. The meaning of the 'value' property is quite like that of the 'balance' property of an ERC-20 token. Each ERC-3525 token has a 'slot' attribute, ensuring that the value of two ERC-3525 tokens with the same slot be treated as fungible, adding fungibility to the value property of the tokens.

ERC-3525 introduces new token transfer models for semi-fungibility, including value transfer between two tokens of the same slot and value transfer from a token to an address.

Motivation

Tokenization is one of the most important trends by which to use and control digital assets in crypto. Traditionally, there are two approaches to do so: fungible and non-fungible tokens. Fungible tokens are generally powered by ERC-20 standard, where every unit of an asset is identical to each other. ERC-20 is a flexible and efficient way to manipulate fungible tokens. Non-fungible tokens are predominantly ERC-721 tokens, powered by a standard capable of distinguishing digital assets from one another based on identity.

However, both have significant drawbacks. For example, ERC-20 requires that users create a separate ERC-20 contract for each individual data structure or combination of customizable properties, resulting in an unrealistic amount of ERC-20 contracts that need to be created in practice. On the other hand, ERC-721 tokens provide no quantitative feature, hence significantly undercutting their computability, liquidity, and manageability. For example, if we are to create financial instruments such as bonds, insurance policy, vesting plans using ERC-721, no standard interfaces are available for us to control the value in them, making it impossible, for example, to transfer a portion of the equity in the contract represented by the token.

A more intuitive and straightforward way to solve the problem is to create a semi-fungible token that has the quantitative features of ERC-20 and qualitative attributes of ERC-721. The backwards-compatibility of such semi-fungible tokens would help utilize existing infrastructures already in use and lead to faster adoption.

For further design motivations, please view papers and documents below:

Articles & Discussions

Specification

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Related Standards

Every ERC-3525 compliant contract must implement the ERC3525, ERC721 and ERC165 interfaces

pragma solidity ^0.8.0;

/**
 * @title ERC-3525 Semi-Fungible Token Standard
 * @dev See https://eips.ethereum.org/EIPS/eip-3525
 * Note: the ERC-165 identifier for this interface is 0xc97ae3d5.
 */
interface IERC3525 /* is IERC165, IERC721 */ {
    /**
     * @dev MUST emit when value of a token is transferred to another token with the same slot,
     *  including zero value transfers (_value == 0) as well as transfers when tokens are created
     *  (`_fromTokenId` == 0) or destroyed (`_toTokenId` == 0).
     * @param _fromTokenId The token id to transfer value from
     * @param _toTokenId The token id to transfer value to
     * @param _value The transferred value
     */
    event TransferValue(uint256 indexed _fromTokenId, uint256 indexed _toTokenId, uint256 _value);

    /**
     * @dev MUST emit when the approval value of a token is set or changed.
     * @param _tokenId The token to approve
     * @param _operator The operator to approve for
     * @param _value The maximum value that `_operator` is allowed to manage
     */
    event ApprovalValue(uint256 indexed _tokenId, address indexed _operator, uint256 _value);
    
    /**
     * @dev MUST emit when the slot of a token is set or changed.
     * @param _tokenId The token of which slot is set or changed
     * @param _oldSlot The previous slot of the token
     * @param _newSlot The updated slot of the token
     */ 
    event SlotChanged(uint256 indexed _tokenId, uint256 indexed _oldSlot, uint256 indexed _newSlot);

    /**
     * @notice Get the number of decimals the token uses for value - e.g. 6, means the user
     *  representation of the value of a token can be calculated by dividing it by 1,000,000.
     *  Considering the compatibility with third-party wallets, this function is defined as
     *  `valueDecimals()` instead of `decimals()` to avoid conflict with ERC20 tokens.
     * @return The number of decimals for value
     */
    function valueDecimals() external view returns (uint8);

    /**
     * @notice Get the value of a token.
     * @param _tokenId The token for which to query the balance
     * @return The value of `_tokenId`
     */
    function balanceOf(uint256 _tokenId) external view returns (uint256);

    /**
     * @notice Get the slot of a token.
     * @param _tokenId The identifier for a token
     * @return The slot of the token
     */
    function slotOf(uint256 _tokenId) external view returns (uint256);

    /**
     * @notice Allow an operator to manage the value of a token, up to the `_value`.
     * @dev MUST revert unless caller is the current owner, an authorized operator, or the approved
     *  address for `_tokenId`.
     *  MUST emit the ApprovalValue event.
     * @param _tokenId The token to approve
     * @param _operator The operator to be approved
     * @param _value The maximum value of `_toTokenId` that `_operator` is allowed to manage
     */
    function approve(
        uint256 _tokenId,
        address _operator,
        uint256 _value
    ) external payable;

    /**
     * @notice Get the maximum value of a token that an operator is allowed to manage.
     * @param _tokenId The token for which to query the allowance
     * @param _operator The address of an operator
     * @return The current approval value of `_tokenId` that `_operator` is allowed to manage
     */
    function allowance(uint256 _tokenId, address _operator) external view returns (uint256);

    /**
     * @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;

    /**
     * @notice Transfer value from a token to another token with the same slot.
     * @dev This function MUST check if the recipient address is a smart contract. If so, it MUST
     *  call `onERC3525Received` on the recipient contract and verify the return value.
     *  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 revert if the recipient contract rejects the transfer.
     *  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
     * @param _data Additional data with no specified format
     */
    function safeTransferFrom(
        uint256 _fromTokenId,
        uint256 _toTokenId,
        uint256 _value,
        bytes calldata _data
    ) external payable;

    /**
     * @notice Transfer value from a specified token to an address. The caller should confirm that
     *  `_to` is capable of receiving ERC3525 tokens.
     * @dev This function MUST create a new ERC3525 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 token which receives the transferred value
     */
    function transferFrom(
        uint256 _fromTokenId,
        address _to,
        uint256 _value
    ) external payable returns (uint256);

    /**
     * @notice Transfer value from a specified token to an address.
     * @dev This function MUST create a new ERC3525 token with the same slot for `_to` to receive
     *  the transferred value.
     *  This function MUST check if the recipient address is a smart contract. If so, it MUST call
     *  `onERC721Received` on the recipient contract and verify the return 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 revert if the recipient contract rejects the transfer.
     *  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
     * @param _data Additional data with no specified format
     * @return ID of the token which receives the transferred value
     */
    function safeTransferFrom(
        uint256 _fromTokenId,
        address _to,
        uint256 _value,
        bytes calldata _data
    ) external payable returns (uint256);
}

The slot's enumeration extension is OPTIONAL for ERC-3525 smart contracts. This allows your contract to publish its full list of SLOT and make them discoverable.

pragma solidity ^0.8.0;

/**
 * @title ERC-3525 Semi-Fungible Token Standard, optional extension for slot enumeration
 * @dev Interfaces for any contract that wants to support enumeration of slots as well as tokens 
 *  with the same slot.
 *  See https://eips.ethereum.org/EIPS/eip-3525
 * Note: the ERC-165 identifier for this interface is 0x3b741b9e.
 */
interface IERC3525SlotEnumerable is IERC3525 /* , IERC721Enumerable */ {

    /**
     * @notice Get the total amount of slots stored by the contract.
     * @return The total amount of slots
     */
    function slotCount() external view returns (uint256);

    /**
     * @notice Get the slot at the specified index of all slots stored by the contract.
     * @param _index The index in the slot list
     * @return The slot at `index` of all slots.
     */
    function slotByIndex(uint256 _index) external view returns (uint256);

    /**
     * @notice Get the total amount of tokens with the same slot.
     * @param _slot The slot to query token supply for
     * @return The total amount of tokens with the specified `_slot`
     */
    function tokenSupplyInSlot(uint256 _slot) external view returns (uint256);

    /**
     * @notice Get the token at the specified index of all tokens with the same slot.
     * @param _slot The slot to query tokens with
     * @param _index The index in the token list of the slot
     * @return The token ID at `_index` of all tokens with `_slot`
     */
    function tokenInSlotByIndex(uint256 _slot, uint256 _index) external view returns (uint256);
}

The slot level approval is OPTIONAL for ERC-3525 smart contracts. This allows any contract that wants to support approval for slots, which allows an operator to manage one's tokens with the same slot.

pragma solidity ^0.8.0;

/**
 * @title ERC-3525 Semi-Fungible Token Standard, optional extension for approval of slot level
 * @dev Interfaces for any contract that wants to support approval of slot level, which allows an
 *  operator to manage one's tokens with the same slot.
 *  See https://eips.ethereum.org/EIPS/eip-3525
 * Note: the ERC-165 identifier for this interface is 0xb688be58.
 */
interface IERC3525SlotApprovable is IERC3525 {
    /**
     * @dev MUST emit when an operator is approved or disapproved to manage all of `_owner`'s
     *  tokens with the same slot.
     * @param _owner The address whose tokens are approved
     * @param _slot The slot to approve, all of `_owner`'s tokens with this slot are approved
     * @param _operator The operator being approved or disapproved
     * @param _approved Identify if `_operator` is approved or disapproved
     */
    event ApprovalForSlot(address indexed _owner, uint256 indexed _slot, address indexed _operator, bool _approved);

    /**
     * @notice Approve or disapprove an operator to manage all of `_owner`'s tokens with the
     *  specified slot.
     * @dev Caller SHOULD be `_owner` or an operator who has been authorized through
     *  `setApprovalForAll`.
     *  MUST emit ApprovalSlot event.
     * @param _owner The address that owns the ERC3525 tokens
     * @param _slot The slot of tokens being queried approval of
     * @param _operator The address for whom to query approval
     * @param _approved Identify if `_operator` would be approved or disapproved
     */
    function setApprovalForSlot(
        address _owner,
        uint256 _slot,
        address _operator,
        bool _approved
    ) external payable;

    /**
     * @notice Query if `_operator` is authorized to manage all of `_owner`'s tokens with the
     *  specified slot.
     * @param _owner The address that owns the ERC3525 tokens
     * @param _slot The slot of tokens being queried approval of
     * @param _operator The address for whom to query approval
     * @return True if `_operator` is authorized to manage all of `_owner`'s tokens with `_slot`,
     *  false otherwise.
     */
    function isApprovedForSlot(
        address _owner,
        uint256 _slot,
        address _operator
    ) external view returns (bool);
}

ERC-3525 Token Receiver

Smart contracts MUST implement all of the functions in the IERC3525Receiver interface to accept transfers. See "Safe Transfer Rules" for further detail.

 pragma solidity ^0.8.0;

/**
 * @title ERC3525 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers from ERC3525 contracts.
 * Note: the ERC-165 identifier for this interface is 0x009ce20b.
 */
interface IERC3525Receiver {
    /**
     * @notice Handle the receipt of an ERC3525 token value.
     * @dev An ERC3525 smart contract MUST call this function on the recipient contract after a 
     *  value transfer (i.e. `safeTransferFrom(uint256,uint256,uint256,bytes)`).
     *  MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,
     *  uint256,bytes)'))`) if the transfer is accepted.
     *  MUST revert or return any value other than 0x009ce20b if the transfer is rejected.
     * @param _operator The address which triggered the transfer
     * @param _fromTokenId The token id to transfer value from
     * @param _toTokenId The token id to transfer value to
     * @param _value The transferred value
     * @param _data Additional data with no specified format
     * @return `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))` 
     *  unless the transfer is rejected.
     */
    function onERC3525Received(address _operator, uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external returns (bytes4);
}

Token Manipulation

Scenarios

Transfer:

Besides ERC-721 compatible token transfer methods, ERC-3525 introduces two new transfer models: value transfer from ID to ID, and value transfer from ID to address.

1. function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable;
   function safeTransferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external payable;
	
2. function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable returns (uint256 toTokenId_);
   function safeTransferFrom(uint256 _fromTokenId, address _to, uint256 _value, bytes calldata _data) external payable returns (uint256 toTokenId_);

The first one allows value transfers from one token (specified by _fromTokenId) to another token (specified by _toTokenId) with the same slot, as a result, the _value will be subtracted from the value of the source token and added to the value of the destination token;

The second one allows value transfers from one token (specified by _fromTokenId) to an address (specified by _to), the value is actually transferred to a token owned by the address, and the id of the destination token should be returned. Further explanation can be found in the 'design decision' section for this method.

Rules

approving rules:

For being compatible with ERC-721, ERC-3525 provides four kinds of approving functions indicating different levels of approvals, which can be described as full level approval, slot level approval, token ID level approval as well as value level approval.

  • setApprovalForAll, inherited from ERC721, 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.
  • 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.
  • The token ID level approve function, inherited from ERC721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner.
  • The value level approve function, overloaded with a _value parameter, SHOULD indicate that the authorized operator is capable of managing the specified maximum value of the specified token owned by the owner.
  • For any approving function, the caller MUST be the owner or has been approved with a higher level of authority.

transferFrom rules:

  • 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:

    • 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.
    • 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 value of _fromTokenId or its allowance to the operator.
    • MUST emit TransferValue event.
  • The transferFrom(uint256 _fromTokenId, address _to, uint256 _value) function, which transfers value from one token ID to an address, SHOULD follow the rule below:

    • MUST create a new ERC3525 token with the same slot to receive the transferred value.
    • 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.
    • 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 value of _fromTokenId or its allowance to the operator.
    • MUST emit Transfer and TransferValue events.

safeTransferFrom rules:

  • safeTransferFrom SHOULD be used to implement the same function as transferFrom, with an extra step to check if the recipient is capable of receiving ERC-3525 tokens by implementing the onERC3525Received interface.
  • MUST follow the above rules set for transferFrom.
  • MUST check if _to is a smart contract (code size > 0). If so, safeTransferFrom MUST call onERC3525Received on _to and MUST revert if the return value does not match bytes4(keccak256("onERC3525Received(address,uint256,uint256,uint256,bytes)"))

Metadata

Metadata Extensions

ERC-3525 metadata extensions are compatible ERC-721 metadata extensions.

The optional ERC3525Metadata extension can be identified with the ERC-165 Standard Interface Detection.

pragma solidity ^0.8.0;

/**
 * @title ERC-3525 Semi-Fungible Token Standard, optional extension for metadata
 * @dev Interfaces for any contract that wants to support query of the Uniform Resource Identifier
 *  (URI) for the ERC3525 contract as well as a specified slot. 
 *  Because of the higher reliability of data stored in smart contracts compared to data stored in 
 *  centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and 
 *  `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing 
 *  to any resource stored in a centralized system. 
 *  See https://eips.ethereum.org/EIPS/eip-3525
 * Note: the ERC-165 identifier for this interface is 0xe1600902.
 */
interface IERC3525Metadata is
    IERC3525 /* , IERC721Metadata */
{
    /**
     * @notice Returns the Uniform Resource Identifier (URI) for the current ERC3525 contract.
     * @dev This function SHOULD return the URI for this contract in JSON format, starting with
     *  header `data:application/json;`.
     *  See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for contract URI.
     * @return The JSON formatted URI of the current ERC3525 contract
     */
    function contractURI() external view returns (string memory);

    /**
     * @notice Returns the Uniform Resource Identifier (URI) for the specified slot.
     * @dev This function SHOULD return the URI for `_slot` in JSON format, starting with header
     *  `data:application/json;`.
     *  See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for slot URI.
     * @return The JSON formatted URI of `_slot`
     */
    function slotURI(uint256 _slot) external view returns (string memory);
}

ERC-3525 Metadata URI JSON Schema

This is the "ERC-3525 Metadata JSON Schema for contractURI()" referenced above.

{
  "title": "Contract Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Contract Name"
    },
    "description": {
      "type": "string",
      "description": "Describes the contract "
    },
    "valueDecimals": {
      "type": "integer",
      "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."
    },
    "properties": {
      "type": "object",
      "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays."
    }
  }
}

This is the "ERC-3525 Metadata JSON Schema for slotURI(uint)" referenced above.

{
  "title": "Slot Metadata",
  "type": "object",
  "properties": {
    "properties": {
      "type": "object",
      "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays."
    }
  }
}

This is the "ERC-3525 Metadata JSON Schema for tokenURI(uint)" referenced above.

{
  "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."
    }
  }
}

Approval

ERC-3525 introduces a new approval model, where users can approve operators for transferring value from a token with certain ID: function approve(address to, uint256 tokenId, uint256 value);

Rationale

Metadata generation

The ERC-3525 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, the implementation should generate metadata directly from contract code rather than giving out a server URL.

Design decision: Value transfer from token to address

The ‘value’ of an ERC-3525 token is a property of the token and is not linked to an address, so to transfer the value to an address would be actually transferring it to a token owned by that address, not the address itself.

From the implementation perspective, the process of transferring values from token to address is as follows: (1) create a new token, (2) transfer the value to the new token from the 'source token', and (3) transfer the new token to the recipient’s address. So that this method is not fully independent from the ID-to-ID transfer method, and can be viewed as syntactic sugar that wraps the three-step process above.

In a special case, if the destination address owns one or more tokens with the same slot value as the source token, this method will have an alternative implemention as follows: (1) find one token owned by the address with the same slot value of the source token, (2) trnasfer the value to the found token. Both implementation should be treated as compliant with this standard.

The purpose of maintaining this function is to maximize the compatibility with most wallet apps, since for most of the token standards, the destination of token transfer are addresses. This syntactic wrapping will help wallet apps easily implement the value transfer function from an ERC-3525 token to any address.

Design decision: Keep unsafe transfer

There are mainly two reasons we keep the unsafe transfer interfaces:

  1. Since ERC-3525 token is ERC-721 compatible, we must keep compatibility for all wallets and contracts that are still calling unsafe transfer interfaces for ERC-721 tokens.
  2. We want to keep the ability that dapps can trigger business logic on contracts by simply transferring ERC-3525 tokens to them, that is, a contract can put business logic in onERC3525Received function so that it can be called whenever a token is transferred using safeTransferFrom. However, in this situation, an approved contract with customized transfer functions like deposit etc. SHOULD never call safeTransferFrom since it will result in confusion that whether onERC3525Received is called by itself or other dapps that call the safeTransferFrom function.

Approval

For maximal semantic compatibility with ERC-721 and simplification of the approval model, we decided to make the relationship between two levels of approval like that:

Approval of an id does not lead to the ability to partially transfer values from this id by the approved operator;
Approval of total values in a token does not lead to the ability to transfer the token entity by the approved operator;
setApprovalForAll will lead to the ability to transfer any tokens from the owner and to partially transfer values from any token.
setApprovalForAll will lead to the ability to approve any tokens of the owner to third parties, as well as the ability to approve partial transfer of values of any token to a third party.

Backwards Compatibility

As mentioned in the beginning, ERC-3525 is an extension interface of ERC-721, and therefore 100% compatible with the latter.

Implementations

Copyright

Copyright and related rights waived via CC0.

@Will42W
Copy link
Contributor Author

Will42W commented Jun 27, 2022

This is the new discussion thread for EIP-3525.

The new version of EIP-3525 proposal has significant changes from the last version of committed document, so we suggest discussing the new version in this new thread, the old issue(#3641) is closed, only as a reference for the document history.

The main changes of this version is as follows:

  1. Adopting the value/transfer conventional model as general ERC standards, remove the split/merge functions that are more related to business logic of apps
  2. Move some functions to optional interfaces, such as enumerable

@MicahZoltu
Copy link
Contributor

Discussion links should be created at Ethereum Magicians. Please create a thread there and update the EIP to point at it.

@Will42W
Copy link
Contributor Author

Will42W commented Jun 28, 2022

Hi, thanks for Micah, I had posted the discussion at Ethereum Magicians, seems it needs some approve to appear, should I post the PR without the discussion URL or just wait for the thread in Ethereum Magicians to be approved?

New to Ethereum Magicians, thanks a lot.

Discussion links should be created at Ethereum Magicians. Please create a thread there and update the EIP to point at it.

@MicahZoltu
Copy link
Contributor

What exactly do you need approval to do? Are you unable to create a thread or something?

@Will42W
Copy link
Contributor Author

Will42W commented Jun 28, 2022

Are you unable to create a thread or something?

Yes, the discussion thread I created on Ethereum Magicians for EIP-3525 is pending to be reviewed by staff members, because it's the first time I create topics on Ethereum Magicians and it seems to have some approval procedures for users' first topic. So do I have to wait for the thread to be approved and then I can quote the thread in the PR of ERC-3525, or I can first submit the new modifications in a PR and later change the discussion-to URL to Ethereum Magicians after the thread is approved?

Thanks a lot.

@MicahZoltu
Copy link
Contributor

You can iterate on it in the meantime. I have reached out to see why there is such a long delay on getting approved.

@lightclient
Copy link
Member

It looks like it is live now. I didn't approve it, so not sure if another person approved it.

@masterayman
Copy link

masterayman commented Oct 22, 2022

Could you kindly explain why behind the following decision for transferFrom function:

This function MUST create a new ERC3525 token with the same slot for `_to` to receive
     *  the transferred value.

The why must be associated with real-life use cases that can leverage such a property of creating new token IDs upon transfer of such a token from one address to another. In my view, the alternative is transfer of ownership instead of creating a new token ID every time transfer happens between two accounts or addresses. Yeah it's how 1155 works, which seems to me pretty sufficient. After all tokens with the same slot can be merged or transferred and why bother to create a new ID out of transfer? What happens to the originator token ID that is left with zero value? It looks to me a new token ID simply binds an address or account with a token, which is really unnecessary at all.

What's more, from engineering or programming point of view, it doesn't have to mimic the real-life implementations in the code. E.g. you don't have to create a new token ID simply to differentiate two tokens having the same slot ID, unless there's specific on-chain reason to leverage such a trait.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants