Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-2569: Saving and Displaying Image Onchain for Universal Tokens

A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens.

Authors Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous), Ran Xing (@lemontreeran)
Created 2020-03-28
Discussion Link https://ethereum-magicians.org/t/erc-2569-saving-and-displaying-image-onchain-for-universal-tokens/4167

Abstract

This set of interfaces allow a smart contract to save an SVG image in Ethereum and to retrieve an SVG image from Ethereum for fungible tokens, non-fungible tokens and tokens based on standards that will be developed in the future.

The interface set has two interfaces: one to save an SVG file in Ethereum and the other to retrieve an SVG file from Ethereum.

Typical applications include but not limited to:

  • A solution for storage of a fungible token’s icon.
  • A solution for storage of a non-fungible token’s icon.
  • A solution for storage of the icon/logo of a DAO’s reputation token.

Motivation

The ERC-721 token standard is a popular standard to define a non-fungible token in Ethereum. This standard is widely used to specify a crypto gift, crypto medal, crypto collectible etc. The most famous use case is the cryptokitty.

In most of these applications an image is attached to an ERC-721 token. For example, in the cryptokitty case each kitty has a unique image. While the token’s code is saved in Ethereum permanently, the image attached to the token is not.

The existing solutions still keep such an image in a centralized server instead of Ethereum. When these applications display an image for a token they retrieve the token’s information from Ethereum and search the centralized server for the token’s associated image by using the token’s information.

Although this is an applicable way to display an image for a token, the image is still vulnerable to risks of being damaged or lost when saved in a centralized server.

Hence we propose a set of interfaces to save an image for a universal token in Ethereum to keep the image permanent and tamper-resistant, and to retrieve an image for a universal token from Ethereum.

Specification

An EIP-2569 compatible contract MUST have a method with the signature getTokenImageSvg(uint256) view returns (string memory) and a method with the signature setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal.

These methods define how a smart contract saves an image for a universal token in Ethereum which keeps the image permanent and tamper-resistant, and how a smart contract retrieves an image from Ethereum for a universal token.

By calling the methods users should access an SVG image.

  • getTokenImageSvg(uint256 tokenId) external view returns (string memory): for an ERC-721 or ERC-1155 token or a token implemented by a contract which has a member “ID” to specify its token type or token index we define an interface to get an SVG image by using the token’s ID number. For an ERC-20 token or a token implemented by a contract which doesn’t have a member “ID” to specify its token type or token index we define an interface to get an SVG image for it if the token has a member variable string to save the image.

It has the following parameter:

tokenId: for a non-fungible token such as an ERC-721 token or a multi-token such as an ERC-1155 token which has a member “ID” to specify its token type or token index our proposed interface assigns an SVG image’s file content to a string variable of the token’s contract and associates the SVG image to this “ID” number. This unique ID is used to access its SVG image in both a “set” operation and a “get” operation. For a fungible token such as an ERC-20 token no such an ID is needed and our proposed interface just assigns an SVG image’s file content to a string variable of the token’s contract.

  • setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal: for an ERC-721 or ERC-1155 token or a token implemented by a contract which has a member “ID” to specify its token type or token index we define an interface to associate an SVG image to the token’s ID number. For an ERC-20 token or a token implemented by a contract which doesn’t have a member “ID” to specify its token type or token index we define an interface to assign an SVG image to a member variable string of this token’s contract.

It has the following two parameters:

tokenId: for a non-fungible token such as an ERC-721 token or a multi-token such as an ERC-1155 token which has a member “ID” to specify its token type or token index our proposed interface assigns an SVG image’s file content to a string variable of the token’s contract and associates the SVG image to this “ID” number. This unique ID is used to access its SVG image in both a “set” operation and a “get” operation. For a fungible token such as an ERC-20 token no such an ID is needed and our proposed interface just assigns an SVG image’s file content to a string variable of the token’s contract.

imageSvg: we use a string variable to save an SVG image file’s content. An SVG image that will be saved in the imageSvg string should include at least two attributes:”name”, “desc”(description).

The procedure to save an image for a token in Ethereum is as follows:

Step1: define a string variable or an array of strings to hold an image or an array of images.

Step 2: define a function to set an (SVG) image’s file content or an array of image file’s contents to the string variable or the array of strings.

Step 1: for a token such as an ERC-721 or ERC-1155 token which has a member variable “ID” to specify a token type or index and a member variable string to keep an (SVG) image associated with the “ID”, retrieve the (SVG) image from Ethereum by calling our proposed “get” interface with the token’s ID; for a token which doesn’t have a member variable “ID” to specify a token type of index but has a member variable string to keep an (SVG) image, retrieve the (SVG) image from Ethereum by calling our proposed “get” without an “ID”.

Rationale

After Bitcoin was created people have found ways to keep information permanent and tamper-resistant by encoding text messages they want to preserve permanently and tamper-resistantly in blockchain transactions. However existing applications only do this for text information and there are no solutions to keep an image permanent and tamper-resistant.

One of the most significant reasons for not doing so is that in general the size of an image is much bigger than the size of a text file, thus the gas needed to save an image in Ethereum would exceed a block’s gas limit.

However this changed a lot after the SVG(Scalable Vector Graphics) specification was developed by W3C since 1999.

The SVG specification offers several advantages (for more details about the advantages please refer to a reference link:https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) over raster images. One of these advantages is its compact file-size.

“Compact file-size – Pixel-based images are saved at a large size from the start because you can only retain the quality when you make the image smaller, but not when you make it larger. This can impact a site’s download speed. Since SVGs are scalable, they can be saved at a minimal file size”.

This feature well fixes the painpoint of saving an image file in Ethereum, therefore we think saving an SVG image in Ethereum is a good solution for keep the image permanent and tamper-resistant.

In most ERC-721 related DAPPs they display an image for a non-fungible token. In most ERC-20 related DAPPs they don’t have an image for a fungible token. We think displaying an image for a token either based on existing token standards such as ERC-20, ERC-721, ERC-1155 or based on future standards is needed in many use cases. Therefore those DAPPs which currently don’t display an image for a token will eventually need such a function.

However with regard to most of the existing DAPPs which can display an image for a token they save such an image in a centralized server which, we think, is just a compromised solution. By utilizing the SVG specification we think converting a token’s image to an SVG image and saving it in Ethereum provides a better solution for DAPPs to access an image for a token.

This solution not only works for tokens based on ERC-721, ERC-1155 and ERC-20 but will work for tokens based on future standards.

Backwards Compatibility

There are no backward compatibility issues.

Reference Implementation

tokenId: a token index in an ERC-721 token or a token type/index in an ERC-1155 token. It is a uint256 variable.

imageSvg: an SVG image’s file content. It is a string variable. Note: the SVG image should include at least three attributes:”name”, “description” and “issuer”.

setTokenImageSvg: interface to set an SVG image to a token with or without an ID number.

getTokenImageSvg: interface to get an SVG image for a token with or without an ID number.

We propose to add three sol files in the existing ERC-721 implementation. Here are the details for the proposed sol files.

// ----- IERC721GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional retrieving SVG image extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
contract IERC721GetImageSvg is IERC721 {
    function getTokenImageSvg(uint256 tokenId) external view returns (string memory);
}


// ----- ERC721GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/GSN/Context.sol";
import "@openzeppelin/contracts/token/ERC721/./ERC721.sol";
import "@openzeppelin/contracts/introspection/ERC165.sol";
import "./IERC721GetImageSvg.sol";

contract ERC721GetImageSvg is Context, ERC165, ERC721, IERC721GetImageSvg {
    // Mapping for token Images
    mapping(uint256 => string) private _tokenImageSvgs;

    /*
     *     bytes4(keccak256('getTokenImageSvg(uint256)')) == 0x87d2f48c
     *
     *     => 0x87d2f48c == 0x87d2f48c
     */
    bytes4 private constant _INTERFACE_ID_ERC721_GET_TOKEN_IMAGE_SVG = 0x87d2f48c;

    /**
     * @dev Constructor function
     */
    constructor () public {
        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721_GET_TOKEN_IMAGE_SVG);
    }

    /**
     * @dev Returns an SVG Image for a given token ID.
     * Throws if the token ID does not exist. May return an empty string.
     * @param tokenId uint256 ID of the token to query
     */
    function getTokenImageSvg(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId), "ERC721GetImageSvg: SVG Image query for nonexistent token");
        return _tokenImageSvgs[tokenId];
    }

    /**
     * @dev Internal function to set the token SVG image for a given token.
     * Reverts if the token ID does not exist.
     * @param tokenId uint256 ID of the token to set its SVG image
     * @param imagesvg string SVG  to assign
     */
    function setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal {
        require(_exists(tokenId), "ERC721GetImageSvg: SVG image set of nonexistent token");
        _tokenImageSvgs[tokenId] = imagesvg;
    }

}


// ----- ERC721ImageSvgMintable.sol -------------------------

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC721/ERC721Metadata.sol";
import "@openzeppelin/contracts/access/roles/MinterRole.sol";
import "./ERC721GetImageSvg.sol";

/**
 * @title ERC721ImageSvgMintable
 * @dev ERC721 minting logic with imagesvg.
 */
contract ERC721ImageSvgMintable is ERC721, ERC721Metadata, ERC721GetImageSvg, MinterRole {
    /**
     * @dev Function to mint tokens.
     * @param to The address that will receive the minted tokens.
     * @param tokenId The token id to mint.
     * @param tokenImageSvg The token SVG image of the minted token.
     * @return A boolean that indicates if the operation was successful.
     */
    function mintWithTokenImageSvg(address to, uint256 tokenId, string memory tokenImageSvg) public onlyMinter returns (bool) {
        _mint(to, tokenId);
        setTokenImageSvg(tokenId, tokenImageSvg);
        return true;
    }
}


We propose to add three sol files in the existing ERC-1155 implementation.
Here are the details for the proposed sol files.

// ----- IERC1155GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;

import "./IERC1155.sol";

/**
 * @title ERC-1155 Multi Token Standard, retrieving SVG image for a token
 * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
 */
contract IERC1155GetImageSvg is IERC1155 {
    function getTokenImageSvg(uint256 tokenId) external view returns (string memory);
}


// ----- ERC1155GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;

import "./ERC1155.sol";
import "./IERC1155GetImageSvg.sol";

contract ERC1155GetImageSvg is ERC165, ERC1155, IERC1155GetImageSvg {
    // Mapping for token Images
    mapping(uint256 => string) private _tokenImageSvgs;

    /*
     *     bytes4(keccak256('getTokenImageSvg(uint256)')) == 0x87d2f48c
     *
     *     => 0x87d2f48c == 0x87d2f48c
     */
    bytes4 private constant _INTERFACE_ID_ERC1155_GET_TOKEN_IMAGE_SVG = 0x87d2f48c;

    /**
     * @dev Constructor function
     */
    constructor () public {
        // register the supported interfaces to conform to ERC1155 via ERC165
        _registerInterface(_INTERFACE_ID_ERC1155_GET_TOKEN_IMAGE_SVG);
    }


    /**
     * @dev Returns an SVG Image for a given token ID.
     * Throws if the token ID does not exist. May return an empty string.
     * @param tokenId uint256 ID of the token to query
     */
    function getTokenImageSvg(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId), "ERC1155GetImageSvg: SVG Image query for nonexistent token");
        return _tokenImageSvgs[tokenId];
    }

    /**
     * @dev Internal function to set the token SVG image for a given token.
     * Reverts if the token ID does not exist.
     * @param tokenId uint256 ID of the token to set its SVG image
     * @param imagesvg string SVG  to assign
     */
    function setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal {
        require(_exists(tokenId), "ERC1155GetImageSvg: SVG image set of nonexistent token");
        _tokenImageSvgs[tokenId] = imagesvg;
    }

}



// ----- ERC1155MixedFungibleWithSvgMintable.sol -------------------------

pragma solidity ^0.5.0;

import "./ERC1155MixedFungibleMintable.sol";
import "./ERC1155GetImageSvg.sol";

/**
    @dev Mintable form of ERC1155 with SVG images
    Shows how easy it is to mint new items with SVG images
*/

contract ERC1155MixedFungibleWithSvgMintable is ERC1155, ERC1155MixedFungibleMintable, ERC1155GetImageSvg {
    /**
     * @dev Function to mint non-fungible tokens.
     * @param _to The address that will receive the minted tokens.
     * @param _type The token type to mint.
     * @param tokenImageSvg The token SVG image of the minted token.
     */
    function mintNonFungibleWithImageSvg(uint256 _type, address[] calldata _to, string memory tokenImageSvg) external creatorOnly(_type) {
        mintNonFungible(_type, _to);
        setTokenImageSvg(_type, tokenImageSvg);
    }


    /**
     * @dev Function to mint fungible tokens.
     * @param _to The address that will receive the minted tokens.
     * @param _id The token type to mint.
     * @param _quantities The number of tokens for a type to mint.
     * @param tokenImageSvg The token SVG image of the minted token.
     */
    function mintFungibleWithImageSvg(uint256 _id, address[] calldata _to, uint256[] calldata _quantities, string memory tokenImageSvg) external creatorOnly(_id) {
        mintFungible(_id, _to, _quantities, tokenImageSvg)  {
        setTokenImageSvg(_id, tokenImageSvg);
    }
}



We propose to add three sol files in the existing ERC-20 implementation.
Here are the details for the proposed sol files.


// ----- IERC20GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title ERC-20 Fungible Token Standard, retrieving SVG image for a token
 * @dev See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
 */
contract IERC20GetImageSvg is IERC20 {
    function getTokenImageSvg() external view returns (string memory);
}


// ----- ERC20GetImageSvg.sol -------------------------

pragma solidity ^0.5.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IERC20GetImageSvg.sol";

contract ERC20GetImageSvg is ERC20, IERC20GetImageSvg {
    string private _tokenImageSvg;
//将图片实现写在构造器中
    constructor(string calldata svgCode) public {
_tokenImageSvg = svgCode
}

    /**
     * @dev Returns an SVG Image.
     */
    function getTokenImageSvg() external view returns (string memory) {
        return _tokenImageSvg;
    }

}


Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous), Ran Xing (@lemontreeran), "ERC-2569: Saving and Displaying Image Onchain for Universal Tokens [DRAFT]," Ethereum Improvement Proposals, no. 2569, March 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2569.