forked from DecentralizedClimateFoundation/DCIPs
354 lines
17 KiB
Markdown
354 lines
17 KiB
Markdown
|
---
|
|||
|
eip: 2569
|
|||
|
title: Saving and Displaying Image Onchain for Universal Tokens
|
|||
|
description: A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens.
|
|||
|
author: Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous), Ran Xing (@lemontreeran)
|
|||
|
discussions-to: https://ethereum-magicians.org/t/erc-2569-saving-and-displaying-image-onchain-for-universal-tokens/4167
|
|||
|
status: Stagnant
|
|||
|
type: Standards Track
|
|||
|
category: ERC
|
|||
|
created: 2020-03-28
|
|||
|
---
|
|||
|
|
|||
|
## 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](https://www.cryptokitties.co/).
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
```solidity
|
|||
|
// ----- 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
|
|||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|
|||
|
|