From 8af67ea6f5b5b17f31e43fbb8378926b287110e9 Mon Sep 17 00:00:00 2001 From: SoohanPark <725psh@gmail.com> Date: Wed, 23 Feb 2022 17:00:23 +0900 Subject: [PATCH 01/12] Initial Commit Initial commit for EOSIS (Expandable Onchain SVG Images Storage Structure) --- EIPS/eip-eosis.md | 425 +++++++++++++++++ assets/eip-eosis/StructureDiagram.jpg | Bin 0 -> 23474 bytes assets/eip-eosis/eip-eosis_KR.md | 429 ++++++++++++++++++ .../implementation/AssembleContract.sol | 71 +++ .../implementation/IAssembleContract.sol | 22 + .../implementation/IStorageContract.sol | 16 + .../implementation/PropertyContract.sol | 54 +++ .../implementation/StorageContract.sol | 28 ++ 8 files changed, 1045 insertions(+) create mode 100644 EIPS/eip-eosis.md create mode 100644 assets/eip-eosis/StructureDiagram.jpg create mode 100644 assets/eip-eosis/eip-eosis_KR.md create mode 100644 assets/eip-eosis/implementation/AssembleContract.sol create mode 100644 assets/eip-eosis/implementation/IAssembleContract.sol create mode 100644 assets/eip-eosis/implementation/IStorageContract.sol create mode 100644 assets/eip-eosis/implementation/PropertyContract.sol create mode 100644 assets/eip-eosis/implementation/StorageContract.sol diff --git a/EIPS/eip-eosis.md b/EIPS/eip-eosis.md new file mode 100644 index 00000000000000..acdc15d066ee59 --- /dev/null +++ b/EIPS/eip-eosis.md @@ -0,0 +1,425 @@ +--- +eip: +title: Expandable Onchain SVG Images Storage Structure +description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. +author: KyungEun Kim , Soohan Park <725psh@gmail.com> +discussions-to: https://ethereum-magicians.org/t/expandable-onchain-svg-images-storage-structure/8330 +status: Draft +type: Standards Track +category: ERC +created: 2022-02-23 +--- + + + +## Simple Summary +It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. + + + +## Abstract +This standard proposal is a Expandable Onchain SVG Images Storage Structure Model on the Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. + +It is a structure designed to store SVG images with a larger capacity by distributed SVG images in units of tags on Ethereum. + +The structure presented by this EIP consists of a total of three layers as shown below. + +![StructureDiagram.jpg](../assets/eip-eosis/StructureDiagram.jpg) + +> **Storage Layer ─** A contract layer that stores distributed SVG images by tags. +> **Assemble Layer ─** A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. +> **Property Layer ─** A contract layer that stores the attribute values for which SVG tag to use. + +It is designed to flexibly store and utilize larger capacity SVG images by interacting with the above three layer-by-layer contracts each other. + +Also, you can configure the Onchain NFT Images Storage by adjusting the Assemble Layer's contract like below. + +* A storage with expandability by allowing additional deployment on Storage Layer's contracts +* A storage with immutability after initial deployment + +Additionally, this standard proposal focuses on, but is not limited to, compatibility with the [EIP-721](/EIPS/eip-721.md) standard. + + + +## Motivation +Most NFT projects store their NFT metadata on a centralized server rather than on the Ethereum. Although this method is the cheapest and easiest way to store and display the content of the NFT, there is a risk of corruption or loss of the NFT's metadata. In addition, even in the case of IPFS, tampering of contents can be prevented, but contents could be lost if there is no node storing the contents. + +To solve this problem, most NFT metadata is stored on Ethereum. However, it can only be expressed as a simple shape such as a circle or a rectangle, since one contract can be distributed 24KB size for maximum. + +We propose this model *─ a more secure way to store NFT metadata ─* to create and own high-quality of NFT metadata. + + + +## Specification +The key words “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. + +We would like to explain each of the three layers of the proposed model in detail. + +After classifying layers according to functions and roles and distributing them to different contracts, these contracts are logically connected to be used like a contract with one large storage space. + +### Storage Layer +A contract layer that stores distributed SVG images by tags. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title IStorageContract + * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. + * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. + */ +interface IStorageContract { + /** + * @notice Returns the SVG image tag corresponding to `assetId_`. + * @param assetId_ Asset ID + * @return A SVG image tag of type String. + */ + function getAsset(uint256 assetId_) external view returns (string memory); +} +``` + +After these Storage Layer's contracts are deployed, they can only perform the role of delivering the saved SVG image tags to the Assemble Layer. + +Since we **SHOULD** have to consider data contamination, we didn't implement the function of `setAsset`. Therefore, registering SVG image tags is only possible when deploying a contract, and it is **RECOMMENDED** to deploy a new contract if changes are required in the future. + +### Assemble Layer +A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title IAssembleContract + */ +interface IAssembleContract { + /** + * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @param attrs_ Array of corresponding property values sequentially for each connected contract. + * @return A complete SVG image in the form of a String. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * It should be noted that the order in which the asset storage contract is registered must be carefully observed. + */ + function getImage(uint256[] memory attrs_) external view returns (string memory); + + /** + * @notice Returns the count of connected Asset Storages. + * @return Count of Asset Storage. + * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. + */ + function getStorageCount() external view returns (uint256); +} +``` + +The `addStorage(address)` function, add new Storage Layer's contract to Assemble Layer's contract, is not included the interface. Because, we would like to each layer to be isolated from each other. + +e.g. The `addStorage(address)` function can be implemented like this: + +```solidity +/** + * @param storageAddr_ Address of `StorageContract`. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ +function addStorage(address storageAddr_) public virtual returns (uint256) { + _assets.push(AssetStorage({ + addr: storageAddr_, + stock: IStorageContract(storageAddr_) + })); + return _assets.length-1; // index +} +``` + +### Property Layer +A contract layer that stores the attribute values for which SVG tag to use. + +The user can access the saved image through the Property Layer, and inheritance of EIP-721 or other standards is also performed in this layer. + +The following functions are descriptions of the main functions to be implemented in the Property Layer's contract. + +- `getImage(uint256)`: This function gets the saved SVG image. Get the property values corresponding to `tokenId_` and call `getImage` of the Assemble Layer interface. + + ```solidity + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + ``` + +- `setAssembleContract(address)`: This function sets the Assemble Layer's Contract. If you want to use it as an Immutable Storage that cannot be changed after the initial deployment, you can remove the function. + + ```solidity + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + ``` + +- `_setAttr(uint256)`: This is a function that sets the attribute value of which SVG image tag to load. The logic for how to set the property value **MUST** be implemented separately according to the direction to be used. + + ```solidity + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + + /// @dev Implement the property value setting logic. + + _attrs[tokenId_].push(newValue); + } + } + ``` + + + +## Rationale + +### Large Capacity Storage +The best way for us to keep our content permanent and tamper-proof is to store it on Ethereum, rather than on centralized servers or IPFS, where it can be tampered with or lost. Like the SVG format, various extensions have come out to reduce the size of the content, but most of the content still has a size of several MB or more. Through this EIP, we would like to provide a solution that can safely store SVG images in sizes ranging from tens of KB to several MB on Ethereum. + +### Cost Efficiency +The protocol proposed by this EIP requires the deployment of a large number of contracts. Therefore, it is necessary to reconsider using this EIP if the number of SVG images you want to save is small or the size is small enough to include them all in one contract. + +### Expandable +In this EIP, to prevent data contamination, we have placed some restrictions on **EXPANDABLE** as shown below. + +- Storage Layer's contracts are set to **be written only once**. Since it is a place where SVG image tags are stored directly, values can only be set at the first deployment to prevent data contamination. If you need to modify the value, you need to deploy a new contract. +- When connecting the Storage Layer to the Assemble Layer, it is set to **be appended only**. By designing existing connected contracts not to change, we tried to minimize data contamination while maintaining scalability. + + + +## Backwards Compatibility +There are no backward compatibility issues. + + + +## Reference Implementation +### PropertyContract.sol +```solidity +pragma solidity ^0.8.0; + +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {IAssembleContract} from "./IAssembleContract.sol"; + +/** + * @title PropertyContract + * @notice A contract that stores property values. + */ +contract PropertyContract is ERC721 { + /** + * @notice A variable that stores the object of `AssembleContract`. + */ + IAssembleContract public assembleContract; + + // Storing property values corresponding to each number of storage. (tokenId -> attr[]) + mapping(uint256 => uint256[]) private _attrs; + + /** + * @dev `name_` and `symbol_` are passed to ERC-721, and in case of `assembleContractAddr_`, the `setAssembleContract` function is used. + */ + constructor(string memory name_, string memory symbol_, address assembleContractAddr_) ERC721(name_, symbol_) { + setAssembleContract(assembleContractAddr_); + } + + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + + /// @dev Implement the property value setting logic. + + _attrs[tokenId_].push(newValue); + } + } +} +``` + +### IAssembleContract.sol +```solidity +pragma solidity ^0.8.0; + +/** + * @title IAssembleContract + */ +interface IAssembleContract { + /** + * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @param attrs_ Array of corresponding property values sequentially for each connected contract. + * @return A complete SVG image in the form of a String. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * It should be noted that the order in which the asset storage contract is registered must be carefully observed. + */ + function getImage(uint256[] memory attrs_) external view returns (string memory); + + /** + * @notice Returns the count of connected Asset Storages. + * @return Count of Asset Storage. + * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. + */ + function getStorageCount() external view returns (uint256); +} +``` + +### AssembleContract.sol +```solidity +pragma solidity ^0.8.0; + +import {IAssembleContract} from "./IAssembleContract.sol"; +import {IStorageContract} from "./IStorageContract.sol"; + +/** + * @title AssembleContract + * @notice A contract that assembles SVG images. + */ +contract AssembleContract is IAssembleContract { + + /** + * @dev Asset storage structure. Stores the contract address value and the corresponding object. + */ + struct AssetStorage { + address addr; + IStorageContract stock; + } + + AssetStorage[] private _assets; + + /** + * @dev Register address values of `StorageContract`. Pay attention to the order when registering. + */ + constructor (address[] memory assetStorageAddrList_) { + for (uint256 i=0; i < assetStorageAddrList_.length; i++) { + addStorage(assetStorageAddrList_[i]); + } + } + + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256[] memory attrs_) external view virtual override returns (string memory) { + string memory imageString = ""; + + imageString = string(abi.encodePacked(imageString, "")); + + for (uint256 i=0; i < attrs_.length; i++) { + imageString = string( + abi.encodePacked( + imageString, + _assets[i].stock.getAsset(attrs_[i]) + ) + ); + } + + imageString = string(abi.encodePacked(imageString, '')); + + return imageString; + } + + /** + * See {IAssembleContract-getStorageCount} + */ + function getStorageCount() external view virtual override returns (uint256) { + return _assets.length; + } + + /** + * @param storageAddr_ Address of `StorageContract`. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function addStorage(address storageAddr_) public virtual returns (uint256) { + _assets.push(AssetStorage({ + addr: storageAddr_, + stock: IStorageContract(storageAddr_) + })); + return _assets.length-1; // index + } +} +``` + +### IStorageContract.sol +```solidity +pragma solidity ^0.8.0; + +/** + * @title IStorageContract + * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. + * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. + */ +interface IStorageContract { + /** + * @notice Returns the SVG image tag corresponding to `assetId_`. + * @param assetId_ Asset ID + * @return A SVG image tag of type String. + */ + function getAsset(uint256 assetId_) external view returns (string memory); +} +``` + +### StorageContract.sol +```solidity +pragma solidity ^0.8.0; + +import {IStorageContract} from "./IStorageContract.sol"; + +/** + * @title StorageContract + * @notice A contract that stores SVG image tags. + * @dev See {IStorageContract} + */ +contract StorageContract is IStorageContract { + + // Asset List + mapping(uint256 => string) private _assetList; + + /** + * @dev Write the values of assets (SVG image tags) to be stored in this `StorageContract`. + */ + constructor () { + // Setting Assets such as _assetList[1234] = "2~Axt4qBmG|sIQ9criSq~mfbsD0Mi}TE zIBSDC@ICLhbm7uf&)?ubIkWhF?ETIi0O(Qtlk&gH;&ybpdWnVff%W(CW>wC*Sw|MU z_wo<;+IRTk4>}`qAW#MG9V7bd|@I^M*-^bI3h12{U|B|QocX)^epTxNPey{6$ z`hLf}P98`L*7+>!FAlg0KmZH?IxP6VHEp!#m23du*fIdHHEtvCVln_w5efk8AK1tP zW&!}aLI40-r{@Ll3%}kBC+o=J>E>!9rt+n`0~IHm>agg8Mc6Y0Mpa_DNZt#V76+k`|UZs!RZCoA+>o zRCK*`F0jb=ZgBmL`}bUd-|PB4cL2bA*+A|7 zlN;bROV0c!HBocV<-cO`yMs5t+sHHj;Uw@Qz9zFs3(&wmr)i6-`<>M(#Gmsl$8?=h zW5@MK9jcXPFE8Ll?NjCj63|-q2rXHZN$ziAW1TrfjB*{_Q{zj5c`sjYrIS(CswfG~ zG~GM8w!l`3PYV^rRL_uN9=l?Ey|V^5h24Qg@pjgIxFWq;+*&W@x^!O1OznBi+y6D$<&EcbFK*-B#RinWVh%yf{Cj1 z*SZ(NEYW^$jSyqvvajJ%GWtl~?F~8Ya8gc=FY$o1@*4claQZ)~8vd7t6X@)fYmW!R zcYn6JE1!)FTZu}myt51MazH-whXgrx4OGim&d=E0Ab3~YvrJj;9ab(sw+`qd&u5EU z53K{n)7uI(NEjNb)nIjE9pH7U_`0#9rrYkqFW0>mO7|&gr=t5;>;|F(FA1Fu&n-ej zy_&uDw#CpEO(7a0juCqOg3K_{2-63L z7s)}skqk*Oh3?B(miavL@>y_@ot1rub)uOwGU}{%g^Awf-Gt$rUXs!1Yq(I+_&ll0 zS+WVe!1Odx!>2CK(XHgESxecmPci)D04;RUV3WS5d_9(qu;0tVo&#kV#vx~4F$vAqi zNg}>S8B+(!|ag25Prn^&OcjR?3@Jv;*{h+l1|JKh|= zRkqKCN8QkaXWpJVE0M~P?bu%!b*=nO0Ln;tSfKlJ`d&W{>-K zFW8q`Q*x_4x*W}Ha*u<<(974_mEJf9WX1+R1`I6+u=~5_8Qp|UnHOSrs9f2 z4|*?SMX_Q?4@1}+whp)mx798*C@7R)Ts%E-%s%nR)ucTl)IQ1yf!1;uNxsnRtHIFC zFw3cR0A7S_2NNDMQYIwp;6l)Fr&Wt@I}`MNE>VnnQQ+lX&208DJg6A&@tkbHv|$D$2Ua`$f*!4m_iY)a$x8z_Mx;E0#2?p1UID)}L35{<3N=kdcaxEcKhMKAQ(|ju%pC?_IiT zain8zrq?N}DYXa~r7c4Km|+vK3N)X+v3pISIyWye&7`C{qMw0w9-X|3{d|0P_Q}Dy1<}q3{gkJVgB(g;mobA5WA>RGjF9Anaixa6`w`xI(L(EF6)ZAiXy0 z`h9)yYrdM!89m1RdHZ1 z=VScPIQrYsk1~q=D;!b`(w<^|9mmGIi%`AFrYvJhe;gT0Oil&H4h2*zRKyf}BfLSK z))in+@cuPmzp_n~x9T^0(zCc+%f^B09m?ZKN()zL<}1hL=omV}M?!sk<~7ECWm$IR z@xv1(WXTAmYc*G>lHTQC3@Efdzhpi|ck$el^UhmUmtG)MQJ%iPYL~|`WVN?rrEqz% zec_WLGc^CR^7jx|#-cuwj;Y{5ABlad8u)Dt((R&32$jP@PF(jdH=b1wxou1<(Nt5WL*|V~JM1NaIL!ZxV4R+7yEjPvd>% zrC7gBpli@jJ;f@UKDsm&v_xba^}DrpCVMbx?q}m~%7avEhmwg3p&og%*Rz}#*gT@1 zzVTh76zAGZ!swvP1+bzHG&x_}cBZK1qj92iLiR$9o8@Xi)xC+bfr93_XXMkWeV+xz z7#H}#4e%%(Xj9=1Sae*-j^LUOYi5%hc?S`Q)m5Qq7A;h~J1I6yU zz@RX%XLP;GnXRAF`K|8gMt|#fsF1ZS_jB`^iV>SP{zxnf&5}jq8fL+o;#OFT%WDlX zSN$$Kf6zqg{L=n;+m~f|MV-_yi~5GK>iU{!jnBIu%w>Xbw41HGgh3ad=(2>6t;+A* z!$I0XMI)zhh=XP>Le(B(r7iOLY33uZc}1}U5hV3_tfkXPP!GuE@WiRZelN>jb|{S| zO537cwVrlRCaKfs^W`apSeq*5;p#p+)k3}QIu8=6v5-&H5O;(45rtBgl=0%ZUk%?y zzppA%;u__hXXTR$6ZJ6i8>@pbp^$}!qQPp#%S^o6P`qOy@G zq1F7EgO-ndCC8JGD+#E5-cI6%lmh3Ekbt<{7?e@N^4$`ATT-Fy`$#;V>lMk^BAiST z!W&^HX^Sz(JY*^{ie$JWS{c>eVnB?~d4W^e-zVAa<&&iif^MW%tpjS`YY=U*v&T&P-ZuGuaZ-lH*A1GMOH??Z z%5CX0__}us^uPgQNM>8t(-zsaTO!oH=)%m)1yS+4BRFxDQ`|2b z8yXXZzIGfW`uj?De0rN>R3}H3kJ`Jskj9g&uIitvBULJpJ{cU7C2B?_p_(&gv)6-0W3I_}ISHk<9HC74R$^KM;pN+!=xutTDQ5Ohaphu;Xp`b8 z@56J=ND+18fz%VyR?`)QEr@Jn53(w^`2 zFF^zgn{3rt&?i&%88okw*73s2yL01n>fW0Lo}}Mma5+4gjr7+97umghJ+RZn-g{QQ zAfp}Y{v>WlH$j4tYiZb-S7C;#vUf^=CcA**v9Z}z)d2{_)Idf)oTf;1tj>o7&JKG(cYOsH!4 z$yFqsH7iHJ!FJ*3P(H9%gY1nI6({aKD=Hle6q28tS@p2LLao-iM7>Y;1)C7!h?V_P<02wijcy8to?b7mQJqdK+~i=CWD8Xr1;Z)L1@Ho~ zD~ieU{JMG!$0H>OdZ1voMV?789j5Xv`)6hKT;tH-G zvOUgt5&y6p=p=$ro}7^$U-X+I*+h*9AOVtoWyPTUIPVu%^82V2MC-AU57|IB?&|A-CA5f7_mLvs-{;Y!4 z7Uc(CD>r%_QPWmbC|HpjW>_fwC;k(b=(fJ-B>>C4`E5Fu1DCCqBEMcA*{dfVaox(L zU6ki#_MsS~C%85@2Yfy}5?auVO|YUO^e5ukrhmQ^uRhAc!@+&l=4_yl*%tYJvP594 z{I_CX-`yJ^Yn%%f{zsDtc)TFIIo17m(Y^u}Zy5wsSUaxz29YmM0;4{5@Le8(;>KdD z-^Jl6J1ToUY$hH{X8$B@#`s^i9d}cW z4+6&v5^(*OPmRzD)tOb&=HRjc(5aAIu?v>+c1>VdvPs+mft*|8?EE%Hl&FW|V%w_- z=6k>Igcrw8@?H9yZD)kCbi|+UfP7ag8`~aiT>np!Y-1>`1DGW@Nq(<=^e)e@4u{QM4V#txpxacEn~_m( zZ;-Iv#bz#$4dmaT%^0)a&nAp-|EPSE(n!jVj!P$o^#g`2#WciqANI@`oOP`maHD!u zW_uAeO>}azRWjm6vX_8EHxp2CqX+{kq{vQ*PSbc}KhyV19~xbrKWjUCa7jLDLH^2P zi3+UwSv`;W<+V4H7_3jrj`zECEFmiW!Mv}Ii3Di1XBaTA zaxO?SO1tu&U}B5GxQprXmYU0&7bg*zmvKc0-&FhR`(ch`_kijk4RDx**O)HE96Qj9 zD;Qxg%qOgZWuzL2A0r!)Nyxxx+sNwzE^(2>)l!P@4s?(-!9_1TY<0)iXShOc*w{*h zc^+qimgD4$OJ8ODQNCx@CVi`5t_F~z3h+c>9yG>0&s48;PLL>Et(Xkfv_vDy5zbh; zBjS1W>G6E$SY+K5&6HP^#+*h)D~}VAv)=X)kah&7#;6dl*>)J519~3$A}p%Cn%+&> z=XXN)VuUKU^-iG_>OiQOK4=2u!Kk)5jz+h$9oM>vO+xx%0jM?Jht7a3&Z zG4`$`+O_A(p!*p=;$s(`@D>8yxkObi0*dmrOwb9(!DOKEA|kugpEf1$Q>VKgm(B5b zY(PyLjr^vLedAr>9s~zLJ$8nF?M-&h5s$EcO>&D2_Do%L#Fm(&FelX+Mj{6V>?GRH zF|%Gcd;Lr?PBK0Y_E2oa{zEZb^)qT~2mA<6nWFZHvZkR$PxKD-yH^Q@ zsS<~eOgFZ9U-FM8FXZGk025~XVoZeA-g3YA(t?66V+&VkQ?Y~_vQ5E_J?-4^z#nNhd#YSBhC+&C6L?V7F^A3 zD7tYodB8F5W78I%anSq00YFkdJQu|q?74zo<Xq*oll;;g^dg)GxJ`~r zgpGmw`nBaEuBeBqhXyfOo;6)^!9|5`>xPP(paGc)`}d-+;ew#5mNgLs&sL%vaO{Ws;6jE6254tw1s!x#KBi{^{H#g z&VwFeDVAj^g6F}$;*(K%(cQp$ubQRncU4p_8s0Nb1QT*jIvSq_JwqX+RdW^&0>#iO zAmKEy^5G&-tEX}YKiT#UB+tUaxX@B=%EJeIlu;V}W#HhRS7rUla#)xaLK&J&Z10dg zC@d`9eztHga3I!ZdEW>PEd0h(ZAMFcJfF4^d;VTH>rs?_!sB#U>Nz-+UyS zlzumfHe4=$>Rb6umHyp%6EgPsdb8P?ecJy!uB^*V82|3;VWDis_{X%rS?S-Z{iz8o z2Q9pu1y+>3^+D&S*sI3N_C;N<9`?8_$|VlV=bWXDA(Ct@q_gEy85dR@ZVbd#r1}ux zz(+U{*JK5g$9BBoZQ2CvQShT9A*-h*$V_TQg0CyGSkUQI-|b=5vBz8|$-66f;KNg` zFuHM88JMpG3c;!tbe`Srb?-`k$ttPujf2&ay!aR76D30UQR0L9lLY!e>Ot`}=83FZ z^=9QemaxWpsHs=c!^xL#?Kt82_)F}YwOZTwv)w~0H#-BvdRU%buW6ULrRe~4&$pdt z=sz0qpU-uhm407uvh63ZO?T{b?;#HZ zwi1C?<-eKw`uxHMvO*pTH}Lp2z<(DB+el%nUEHK|ap+;+!tzK5|1V?h3RaL+;9`Gjj}g{wk}@y21a89pq2C^N*~}JM1PUf3p%p z5$NLwL5>e{+m3%+E=SV}y9FbRbGyMypd*7kS%|k_m}RTaJzTk#+L(%lstZ$Ex=U4( zZ~UC!aqBURL-hWQSc6QE!$v+EihL#q@|O|)l}q>_b>_sA4e%)y@b4$tuaxD#R+uoW zKwG5_*FUumR;}`dLM6iRS(C97r)Ka}N66(&q+b5-OQkc&Gv8Yq z_GrWN!dA-isr;TeH;D8t+40Y?P;~NCA0%K)Oe~xptOgF04O&e^=;ijf;JyeZVM5LF zuI?qtJ89~+qS905lT%YvUE>8@_OY!jceBMcv4P*c2W)Xo8{h@jRQcyoQU5-YC%!xD zm)$ea_7%fcvulDiBBUOwnqgNm{!@|Jq=j)gPHslEaF@|7?G?vD3uzTC4k~*XOD3z) z*suRAtc8WbvEi+eW~b+%vI+qlLQvxP13cC{_0YS)US{qE0xTmzph)S%=qe=SWtN@K17uLA z_gMHWBE#rHWI&sD5?#z90cUV1Ro7`G3mAGR0}-o(GTV7%$Ek@9O@Uji36@e_o)8ykt z$Kgkr@OZ@iP{j!s)f#VZN8>8gw9}!KR0Ooa>h2K=U~&2;^>PPg5J@=}IqT6?I4EQr z@BK>u7&##7aw>N2&3@C)ue5rpXfS4QV1|z>{3K6?u!< zl?cp1GL#bMf(UO8k7x`?Pw0~PWd~D!rK2LM^h&C{M9~`(N51TXrh56UO;%Jjk>w*x zVecN`j*54av z5h*RQ45C|B584?okeNsfdzO*jV~%W+l$5I@!Dy!?Pd>W@37JYuUp%B4juCW{#7{Lt zN~z5IG02n=82s#0Du>O!(-v=(LB_C9Q2&Yeyz z^7i#LiZXdTwTpQ#QDG$=QDpP*rCOxEWX73XWKJ99QQNLPbm1>{PQ9zk#~}9I0=ML% z;2x62#6h)fdh9xvO-KKU1~+){`fa;;u*b=HU#F<4L`=^*Aa-=cK=IW(Lj(rJonqqz z^f;M)Sj&o@)BP^cfh-ppWogyJpb@moob&M>R zfFx;z==La++k1ccu6@3z-C~`zy8TU$pPKA!E0wiT+x9F*J>G_z1tuXc)x^GsPpZuM zO29iPBS0CrL6!kAoQwSX)T3_+J>v7yS7BO%0jBrkg^fRN>pkc6)Yc=*io{7LQfhDz zR&szSoM%P;u7&b>|ccQqtV0e3+ z6S1E1!tllX%S=>hUhlRJFXr6YD@I7^=%kEzK7nNmMUx<-FteLI`EchDut!Sl$1D_Y zv;6sk^H+IMp>uCCjEj|8yrdPA7N-lJ-A_6Kta@@P>?e%r-m?5^TW2@9w{+vDphM9( zXvaQRTg6H>7-JeXbI%|OlI)3~dNf7nDs){;-ZQ5w`wBrv_iAGuL7|5T_yahU(cM-K z)lM|kV0ybj-o@#KI@jOvt`$$=pR97?DF4X@a% z7hX9_<3MBVtd++LA2ZLsXt`$^CBaF~@CqA3n@Zx#LTDxx#38z?G&DZ!6K5R+Rx=|i zW3H@7nQicDrOM(BU6~hhVRbo{e9dt+J$5o>)YM%K;ulm{pqX4<|KA9JIoHwD8qu5kTS_mTXR z0*^J)98dbty(QwkMp1a{I>6#pWsqZV0O7$UyCr{e7K#JQA#~#-KHuU{Ra3X)=!q&y zyG3-BzJ9M%P{!E{-h&sOi`ca5q;@|ff#Zk3flxzu1W3g0QdLC~=ABIC(;;oJ@iDZT zy0ZgX%R+VX@I)YQ+ZylB@PA@u{lUBncp>TDbWDA$bD!1Ox;($()1TH3`*;tp`Gm?c zGm}XK1Z1pLdXb5d*OCkb1RmjhUfO#t%P;m(S_=*T7)2WFWDs?T^+*hDDDTJ%DiT~K z1!KAozqge(pGLd1HQ-broHdS2Y^(EN=2RPfGZUAl<;It^}`-#;&>%P{};e$DY-&m#^^trRRd} zNx8ib%deDWbWsmC=hcn-jX?D;2vk4sTTLXi}K`NT(|0 zG5V0DEB2CXpfyZRa6dM8?D3~5MXphC*g4Qo@mZ%CT=Nemqe#$-i#nAVgjuFgf7#P0 zBf@7fK1Dg-=P`UD5V#u(QF`6N+<>K4qVMr13@8X#T|iD9^#IT4XY0r(%)Q8BNRd88 z+GGhzi_OR^iXzHD3b7*~2vN{zIe~mU_Cw4tnt8sD)H?~zlMt;MB4^V`vT6{LmSATk zEDkk8b0kihlVD6}3LF{_mz-e@=fV^%tIsu4Zb3PMvAfG{D(4i+TYUH9xj?jeWt!#m zp_<(2L`elr(ZkB`;gC%Gm$Q74z%G#8AuHE>S0U}aiJACn#5dDL%kGor38r(W6+IGQ zpcKLGJTOQkUJ#k1^8oKu9h=^IG~c)$Q@mvW`zDLEfZ6>=Nwt;7gWbdmE|dp9bPTFR&r*>t*W&vUz}1gl^$pW-35A^#JHNPl&hl0vN{*L=XSJrL`X}h+vTF7 z=O@sgm6Yl60;Hefj~^I>Cd-vgLgR0GDOubhxcF)g@u4D(=**%p7t@a-A=ZudV#(OfR%pC54p7Xt2AQmpI~CWcA0K4G@+P ztwj&E(e|0A>U`~Wdg{fS5Gl)}3q#ZG{CY$EsW4M0m|x(t4Pg6&ouy8_*4td^IrdcI z*yoS6JcEP?^F2zIpSo`W(`8wv+Jnou#NGH~v|%XeTrsCpfqke(iGGD)D^{6LEgzKh z-Za_jAo5b9-!EVDcG!S<@+->^&(q0!i6lNLdKYeGAOwBl69D(5anJ0vk3foA2y)NG zK?FrGbL~O;!lQ=hM<`LXW*wPIx9Awrw`DE}Y~bmd>Oty%h=9$1_7WuD&OZOMHJs$1qHeeD`h#{3Q z4cV1}A_Lfn?^Bl~*m8`fy(%HE|2C(y|7FvT(q9HLo@w6Wc^dDCi|tvO77KY1fvSB^ z2)@L!r%lr+5ZsSeE+i;SCL2dsrxcve`j2^ujyG=e}=9ONtfTd^9I1 z^jvkhWJu;}gR&Op!=`TGAKv6$Qug+7kNjQT8jk6;TJ=Qb>C|PHnil?ieg553=m0~e zD|AA&P}8W>ys?ne`EowF-QPaFn&U!&!G#kS$4}n6uPpP3iV!FDPFYzV?zG&lW)(&{ zHfm>4l+|<+>p%-O@Y92KXXd?Rcc51Y*Aiq7Ow8gw$c-utn1IH+eMW0e0HU>LJ|jZGatb5LlP!J&_@TA9 z>{|<9U`X;x?OIU|1qOGd z&@Uek#t9(NYSExb<+@wS(2(WwMXN_q;HC@qSLf-74S5}4KV0h~J^4F=}yBFaP z-i=Qt=M9s%WpC|`k*&oziCU&Q`qUA`jL2eMquP6-FkB&dsFUqjXJ@R+T8MMt>G5>{ z<;$iz;@60a1tDht&h37=H7X%SB+RiYoWhiX>X)JI^^!M$X@xpkI~;;xJ;e59aab3` zIk4)+^M~w?38U*;ew+$Y zU@eC0{!Dm6j#__&xnvx}lHChk1tz4|0SPK9DyLb0RyvAdTUz~v3-L$j{2zBicR4#7 zWq32tqVpJQX-6J?NIp7?x7p&X$8>J`Z{^G+$q1Q6VD1I#^n{%ZV7R zJL(r$1CBWrdCK~t7!IWmiFd5AlyK@6IBJL(PRNB6JQtJg3hMxgM~E7>KmvbO+g~(S zNA(BwJD$5bp_WW*h~K%KB6M`>hJo?Nn9CwYCuDWv79F98`V>cWKxtQ+<~E?|qmDcN zV{X2F%ofi9reTMIgFJ<>M0M?ORk3){Vi&Q{2YM$QIOWN}i^f25%+uqwzpnuQYCD;& zx!54lq1xGZUj zEQz>H3#MuTb*;;2noDnyU(%;0f0oY#*}^Y9G;unYH&^sPYy;$=av(SGo@{{sE)up4 z0;!GK8zTDGuH(O~IaK#&4eg!6Jh^dHDaE%+d;q?1)?*|GVv!enG&ddS-#wx&*QMt? zP=?-e{<|bLb9J+a1N@;~*@F%Lo?h}+g}45}eQ(5@cxKB>wy({7xMxW=B)wE?EwC}a zbuv2~P52wF`+rkIZ`K30{QlmtG*#wX#=FPo9=@5b zO_6wFk~_4oL2#06o7F+A2ZhZ}sXA#UEsCN?4Q@(|3&wClF!_-9@2$RBqyJD2ECg2C zq|yH@a^!!y`u}EE|5xPkLn?nbZbtU6j-5X+W53vp@xR_b*tz`y-lSXorEdKL6ZVVE z8UBv7+2gRc)9=W?1Di1ZFN7TN5b53~u$JS-gsZdDu00srY>;58akj+->;g?lht!V1 z<>4lGfn5XLo;iECbW`>8GhNueh4P1Y5dJR;7T$blvlVA&kqd4@gw=O0}Y?4^I{()e9$e-CWp z3Hf!s^f%nigIxZe8T<7njQ{o=zhZ92_{Xw$vr_8q=w6jthIJZKyVR>u^V_LL-?04I z+^XE^$b+J~#Bq67nd6`NrVi^EKkrw|;|Bo)iLP&+&MI6ALXLC<@%a%_QFY8Z`Yjzw zy>#ws{9ph`C zcEIVNJ($LGfn|!aqr!+BN;@64*5MR1_41zF(`xyBF{NKs%iu_Plrph!FYpaq5F}(BU?%pA<$J@+4oC2e2H93vT08gfC@R~IyQ`LE^+~F# zI*BM2qG27g#`=<>F3QT{nx-xV`Zl4~l%`5@v2er=s%f?6J+rXHQLuA}Vfd9tvF&JB z7p~~^HN0pXRHfJC!Q0bMHGIj)V24CoVlL6gb=o^UasZda6vZvUPra7cb}Q+}CXgU_ z1*9xmQC9!Z_0lyM&1Kp4?P6ZxmuT2Y9j5bGWl8Ne7eIzZysWNy7Fszm9ZSb1r8w+an5>B zs~8?{Bw_BfdOM+g;&=5V`1NjI5gsD|{GFMnYN9#CqAP$pZ0li|&VP;;&+4 zbFi-J)40IA5xIvwXeDa}zvts6mV{p_V>{3U*Q3q&5mBp3iUxPRI#dB9l-tvQ(R+gR z5_&S!^bIBc_OdunNPkEVmN43jPEdOeJeh3~<~$ZDg9>L-?`rAnBC) zOTYP9HKLfl$X{brA**q%^_INm17+x`=hwO?W%|0`9{aew=M=Lg>Z+7s8QACSk&y^# zU;Y~r-qX3cIJz*+A)0=EU$Q0|^Nv}dch?gOA$8__ywSScijmzmCf#}84=>AC4b{By zc$X|mC?(`Le=FAHNW%Sc4Q|e)2@Na^RaWJc*{re4FXIsqR{pXg%T7qJ(nEg`VfLoh zB1$$<>ZrAhQTU6dgD5)Iy*Ul;=;Ap95=A(l;Ftx64`r5n>*o#akur=Mzm|6o%^Wu* zU@~!I@h?4;({ADf!wHKn7B1GahzLDL&A?~UwL&+l{pt4Er$R^Oi}n^v+ZJTJ#vjPk zgb+*W-(q9nFdtV+czAD&Cj7Mh6B~Uit`j9L%<#5|D~*95Z}Ag#`5sw)RyZt3Lq7fP z&9&*z^^*h)?e$|49GWG|k!b!RsVb>M5WXVTX;3Mnq7DrA>Gw`{Re7F896`L`Y$^NSK^zlyL);BwO;07&sERLw)q0 z2mfqR`aL*}%~WOEIR6ne#I8*L0B=V2+gEnJBeQX1{}K<&fA>$)W{lbIXA{PM`^3*C zrGKk->n9S3TdiOT1R{2>?a*<5(1M+VrHQ|~=T^(Z@)=lCX{i|1Rr$MKxL1CR=he?o zA4tQ{uiDd+8BA5ze5jm#^p7ZFahfN{?^T`}a6y}@R%?=BRJKPlF6@k)C=S^7t?SyYyIA zrz5aa(-oDbrHRnP#Rt6q`PZiX!~QT^D9E;7L-aN){r*}L+t$KA&}}NU&B%U)3;rNu z)iftExQNKpqOW z)dn8d2KetHiP+db3frii|KR1xIqT`qf$yE~d0OOEZ2Qnyh3=iWiay+Op*k=|#3c9R zvy^M`uM7e>bF>9gUAu|5zkhV{pGA0XBF4Y9_792t$#D}hcIj^>YxYj`kDNDQ%zi(c zF=p>XEXG_GK#LMBlTgW1hbt#7Q6;Vd4}$bzA}wf7dhKBvRAvE{Fpj@f{P{NX_8)I! z6C?BgMRWMK2j|T-Bs&JC@tnMI&WlbdcVx&M`x=Q;!O~xo0?)Z)4aUn4w#csoE-i`{ z<68F4#9Cw-#750&B83Z5LN5Olj|lTd*{ME+&teU~5{REc=VpS=tDXOi`H%DFTKs?| z?>%@^!0E&K)w9=^TI>CF^Lrc-?yzA8)ufCDM)F#VZ9b0BXeQ` zu*uN<9womqV%UZ*YXR^dP7;3}e8p<|vFp4?ls(x(3Em%3R$9>QL?;^FAFelutT-)c zi>bk_%x7qe#-Xm4D$`NC)0u2i{9h527Gy&h|098asRQz#;vg)0R1+QwPm~Y%Dv#GD zrNT8kgze?^!eWNZ0q{! zjq+D~G;7uMWg82bW6+4NT87bX@DJIVz1Tp73DuMcJxn@QT3My;e}>IQNBvi|hVcKM Opx9*g?*UJ*_y2$IMF>s+ literal 0 HcmV?d00001 diff --git a/assets/eip-eosis/eip-eosis_KR.md b/assets/eip-eosis/eip-eosis_KR.md new file mode 100644 index 00000000000000..2827a878f2b1d1 --- /dev/null +++ b/assets/eip-eosis/eip-eosis_KR.md @@ -0,0 +1,429 @@ +--- +eip: +title: Expandable Onchain SVG Images Storage Structure +description: It is a Expandable Onchain SVG Images Storage Structure Model on the Ethereum. +author: , Soohan Park <725psh@gmail.com> +discussions-to: https://ethereum-magicians.org/t/expandable-onchain-svg-images-storage-structure/8330 +status: Draft +type: Standards Track +category: ERC +created: +requires: 721 +--- + +## Simple Summary +이더리움에서 확장 가능한 온체인 SVG 이미지 저장 구조 모델입니다. + + + +## Abstract +이 표준 제안은 이더리움 온체인 상에 이미지를 영구적으로 보존하고 변조를 방지하며, 나아가 더 큰 용량의 이미지를 저장할 수 있고 확장 가능한 SVG 이미지 분산 저장 구조 모델입니다. + +SVG 이미지를 태그 단위로 분산 저장하여 온체인 상에서 더 큰 용량의 SVG 이미지를 저장할 수 있도록 설계한 구조입니다. + +이 EIP에서 제시하는 구조는 아래와 같이 총 3개의 레이어로 구성되어 있습니다. + +![StructureDiagram.jpg](./StructureDiagram.jpg) + +> **Storage Layer ─** SVG 이미지를 태그별로 분산 저장하는 컨트랙트 레이어. +> **Assemble Layer ─** 'Storage Layer Contract'에 저장되어 있는 태그들을 조합하여 SVG 이미지를 생성하는 컨트랙트 레이어. +> **Property Layer ─** 어떠한 SVG 태그를 사용할 지, 그에 대한 속성값이 저장되어 있는 컨트랙트 레이어. + +위 3개의 레이어별 컨트랙트들이 서로 상호작용하며 큰 크기의 SVG 이미지를 유연하게 저장하고 활용할 수 있도록 설계하였습니다. + +또한, 아래와 같이 Assemble Layer의 컨트랙트를 조정하여 Onchain NFT Images Storage를 구성할 수 있습니다. + +- A storage with expandability by allowing additional deployment on Storage Layer's contracts +- A storage with immutability after initial deployment + +추가적으로, 이 표준 제안은 EIP-721 표준과의 호환성에 중점을 두고 있지만, 이에 국한되지는 않습니다. + + + +## Motivation +대다수의 NFT 프로젝트들은 자신들의 NFT 메타데이터와 컨텐츠들을 이더리움 네트워크가 아닌 중앙 집중식 서버에 보관하고 있습니다. 이 방법은 NFT의 컨텐츠를 표시하는 가장 저렴하고 손쉬운 방법이지만, NFT의 메타데이터나 컨텐츠가 손상되거나 손실될 위험이 존재합니다. 또한, IPFS의 경우에도 컨텐츠의 변조를 방지할 수는 있지만, 컨텐츠를 저장하고 있는 노드가 없는 경우 컨텐츠가 손실될 수도 있습니다. + +이 문제를 해결하기 위해 대부분 NFT메타데이터를 이더리움 온체인으로 저장합니다. 그러나 한 컨트랙트에 배포 가능한 최대 크기는 24KB이기 때문에 단순한 도형 ─원이나 사각형─ 으로 표현할 수 밖에 없습니다. + +우리는 고품질의 NFT 메타데이터를 생성하고 소유하기 위해 NFT 메타데이터를 저장하는 보다 안전한 방법인 이 모델을 제안합니다. + + + +## Specification +The key words “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. + +이 EIP에서 제안하는 모델의 3개의 레이어에 대해 각각 구체적으로 설명하려 합니다. + +기능과 역할에 따라 레이어를 구분 짓고 이를 각기 다른 컨트랙트로 배포한 뒤, 이 컨트랙트들을 논리적으로 연결하여 하나의 큰 저장 공간을 갖는 컨트랙트처럼 활용되도록 구성하였습니다. + +### Storage Layer + +SVG 이미지를 태그별로 분산하여 저장하는 컨트랙트 레이어입니다. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title IStorageContract + * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. + * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. + */ +interface IStorageContract { + /** + * @notice Returns the SVG image tag corresponding to `assetId_`. + * @param assetId_ Asset ID + * @return A SVG image tag of type String. + */ + function getAsset(uint256 assetId_) external view returns (string memory); +} +``` + +이 Storage Layer's contracts들은 배포된 이후 오직 저장된 SVG 이미지 태그를 Assemble Layer로 전달해주는 역할만 수행할 수 있습니다. + +우리는 데이터 오염을 고려해야하기 때문에 `setAsset`과 같은 기능을 구현하지 않았습니다. **(SHOULD)** 따라서, SVG 이미지 태그를 등록하는 것은 컨트랙트를 배포할 때만 가능하며, 추후 변경이 필요한 경우 새로운 컨트랙트를 배포하는 것을 추천합니다. **(RECOMMENDED)** + +### Assemble Layer + +저장되어 있는 태그들을 조합하여 이미지를 생성하는 미들맨 역할의 컨트랙트 레이어입니다. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title IAssembleContract + */ +interface IAssembleContract { + /** + * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @param attrs_ Array of corresponding property values sequentially for each connected contract. + * @return A complete SVG image in the form of a String. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * It should be noted that the order in which the asset storage contract is registered must be carefully observed. + */ + function getImage(uint256[] memory attrs_) external view returns (string memory); + + /** + * @notice Returns the count of connected Asset Storages. + * @return Count of Asset Storage. + * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. + */ + function getStorageCount() external view returns (uint256); +} +``` + +Assemble Layer의 컨트랙트에 새로운 Storage Layer 컨트랙트를 추가하는 함수인 `addStorage(address)` 기능은 인터페이스에 포함되어 있지 않습니다. 왜냐하면, 우리는 각 레이어가 서로 격리되어 있기를 원하기 때문입니다. + +e.g. `addStorage(address)` 함수는 다음과 같이 구현할 수 있습니다. + +```solidity +/** + * @param storageAddr_ Address of `StorageContract`. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ +function addStorage(address storageAddr_) public virtual returns (uint256) { + _assets.push(AssetStorage({ + addr: storageAddr_, + stock: IStorageContract(storageAddr_) + })); + return _assets.length-1; // index +} +``` + +### Property Layer + +어떠한 SVG 태그 값들을 가져올 지에 대한 속성 값이 저장되어 있는 컨트랙트 레이어입니다. + +사용자는 Property Layer를 통해 저장된 이미지에 접근할 수 있으며, EIP-721 혹은 이 외의 규격들에 대한 상속 역시 이 레이어에서 하게 됩니다. + +아래의 함수들은 Property Layer's contract에서 구현해야 할 주요 함수들에 대한 설명입니다. + +- `getImage(uint256)`: 저장한 SVG 이미지를 가져오는 함수입니다. `tokenId_`에 해당하는 속성값들을 가져와 Assemble Layer 인터페이스의 getImage를 호출합니다. + + ```solidity + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + ``` + +- `setAssembleContract(address)`: Assemble Layer's Contract를 설정하는 함수입니다. 만약, 최초 배포 후 변경이 불가능한 immutable storage 로 활용하고자 한다면, 해당 함수를 제거하면 됩니다. + + ```solidity + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + ``` + +- `_setAttr(uint256)`: 어떠한 SVG 이미지 태그를 불러올 것인지 속성값을 설정해주는 함수입니다. 어떻게 속성값을 설정해줄지에 대한 로직은 반드시 활용하고자 하는 방향에 맞춰 별도로 구현해주어야 합니다. (**MUST**) + + ```solidity + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + + /// @dev Implement the property value setting logic. + + _attrs[tokenId_].push(newValue); + } + } + ``` + + + +## Rationale + +### Large Capacity Storage + +우리가 콘텐츠를 영구적이고 변조 불가능하게 보관하는 가장 좋은 방법은 변조 혹은 유실될 수 있는 중앙 집중식 서버나 IPFS가 아닌, 온체인 상에 저장하는 것입니다. SVG 형식과 같이 콘텐츠의 용량을 줄이기 위해 다양한 확장들이 나왔지만, 대부분의 콘텐츠들은 아직 수 MB 이상의 크기를 가지고 있습니다. 이 EIP를 통해 수십 KB에서 수 MB에 이르는 큰 크기의 SVG 이미지들을 온체인 상에 안전하게 저장할 수 있는 하나의 솔루션을 제공하고자 합니다. + +### Cost Efficiency + +이 EIP에서 제안하는 방식은 많은 수의 컨트랙트 배포를 필요로 합니다. 따라서, 저장하고자 하는 SVG 이미지의 수가 적거나, 하나의 컨트랙트에 모두 포함시킬 수 있을만큼 크기가 작은 경우에는 이 EIP를 활용하는 것을 다시 한 번 검토해볼 필요가 있습니다. + +### Expandable + +이 EIP에서는 **데이터 오염을 방지하기 위해** 아래와 같이 **확장 가능성**에 대해 약간의 제한을 두었습니다. + +- Storage Layer에 속하는 컨트랙트들을 **WRITE ONLY ONCE**로 설정하였습니다. SVG 이미지 태그들을 직접 저장하고 있는 곳이므로, 데이터 오염을 방지하기 위해 첫 배포시에만 값을 설정할 수 있도록 하였습니다. 만약, 값에 대한 수정이 필요한 경우 새로운 컨트랙트를 배포해야 합니다. +- Assemble Layer와 Storage Layer를 서로 연결할 때는 **APPEND ONLY**로 설정하였습니다. 확장성은 유지하되, 기존에 연결된 컨트랙트들은 변경되지 않도록 설계하여 데이터 오염을 최소화하고자 하였습니다. + + + +## Backwards Compatibility +There are no backward compatibility issues. + + + +## Reference Implementation + +### PropertyContract.sol +```solidity +pragma solidity ^0.8.0; + +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {IAssembleContract} from "./IAssembleContract.sol"; + +/** + * @title PropertyContract + * @notice A contract that stores property values. + */ +contract PropertyContract is ERC721 { + /** + * @notice A variable that stores the object of `AssembleContract`. + */ + IAssembleContract public assembleContract; + + // Storing property values corresponding to each number of storage. (tokenId -> attr[]) + mapping(uint256 => uint256[]) private _attrs; + + /** + * @dev `name_` and `symbol_` are passed to ERC-721, and in case of `assembleContractAddr_`, the `setAssembleContract` function is used. + */ + constructor(string memory name_, string memory symbol_, address assembleContractAddr_) ERC721(name_, symbol_) { + setAssembleContract(assembleContractAddr_); + } + + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + + /// @dev Implement the property value setting logic. + + _attrs[tokenId_].push(newValue); + } + } +} +``` + +### IAssembleContract.sol +```solidity +pragma solidity ^0.8.0; + +/** + * @title IAssembleContract + */ +interface IAssembleContract { + /** + * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @param attrs_ Array of corresponding property values sequentially for each connected contract. + * @return A complete SVG image in the form of a String. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * It should be noted that the order in which the asset storage contract is registered must be carefully observed. + */ + function getImage(uint256[] memory attrs_) external view returns (string memory); + + /** + * @notice Returns the count of connected Asset Storages. + * @return Count of Asset Storage. + * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. + */ + function getStorageCount() external view returns (uint256); +} +``` + +### AssembleContract.sol +```solidity +pragma solidity ^0.8.0; + +import {IAssembleContract} from "./IAssembleContract.sol"; +import {IStorageContract} from "./IStorageContract.sol"; + +/** + * @title AssembleContract + * @notice A contract that assembles SVG images. + */ +contract AssembleContract is IAssembleContract { + + /** + * @dev Asset storage structure. Stores the contract address value and the corresponding object. + */ + struct AssetStorage { + address addr; + IStorageContract stock; + } + + AssetStorage[] private _assets; + + /** + * @dev Register address values of `StorageContract`. Pay attention to the order when registering. + */ + constructor (address[] memory assetStorageAddrList_) { + for (uint256 i=0; i < assetStorageAddrList_.length; i++) { + addStorage(assetStorageAddrList_[i]); + } + } + + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256[] memory attrs_) external view virtual override returns (string memory) { + string memory imageString = ""; + + imageString = string(abi.encodePacked(imageString, "")); + + for (uint256 i=0; i < attrs_.length; i++) { + imageString = string( + abi.encodePacked( + imageString, + _assets[i].stock.getAsset(attrs_[i]) + ) + ); + } + + imageString = string(abi.encodePacked(imageString, '')); + + return imageString; + } + + /** + * See {IAssembleContract-getStorageCount} + */ + function getStorageCount() external view virtual override returns (uint256) { + return _assets.length; + } + + /** + * @param storageAddr_ Address of `StorageContract`. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function addStorage(address storageAddr_) public virtual returns (uint256) { + _assets.push(AssetStorage({ + addr: storageAddr_, + stock: IStorageContract(storageAddr_) + })); + return _assets.length-1; // index + } +} +``` + +### IStorageContract.sol +```solidity +pragma solidity ^0.8.0; + +/** + * @title IStorageContract + * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. + * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. + */ +interface IStorageContract { + /** + * @notice Returns the SVG image tag corresponding to `assetId_`. + * @param assetId_ Asset ID + * @return A SVG image tag of type String. + */ + function getAsset(uint256 assetId_) external view returns (string memory); +} +``` + +### StorageContract.sol +```solidity +pragma solidity ^0.8.0; + +import {IStorageContract} from "./IStorageContract.sol"; + +/** + * @title StorageContract + * @notice A contract that stores SVG image tags. + * @dev See {IStorageContract} + */ +contract StorageContract is IStorageContract { + + // Asset List + mapping(uint256 => string) private _assetList; + + /** + * @dev Write the values of assets (SVG image tags) to be stored in this `StorageContract`. + */ + constructor () { + // Setting Assets such as _assetList[1234] = "")); + + for (uint256 i=0; i < attrs_.length; i++) { + imageString = string( + abi.encodePacked( + imageString, + _assets[i].stock.getAsset(attrs_[i]) + ) + ); + } + + imageString = string(abi.encodePacked(imageString, '')); + + return imageString; + } + + /** + * See {IAssembleContract-getStorageCount} + */ + function getStorageCount() external view virtual override returns (uint256) { + return _assets.length; + } + + /** + * @param storageAddr_ Address of `StorageContract`. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function addStorage(address storageAddr_) public virtual returns (uint256) { + _assets.push(AssetStorage({ + addr: storageAddr_, + stock: IStorageContract(storageAddr_) + })); + return _assets.length-1; // index + } +} \ No newline at end of file diff --git a/assets/eip-eosis/implementation/IAssembleContract.sol b/assets/eip-eosis/implementation/IAssembleContract.sol new file mode 100644 index 00000000000000..dbb8bf2ccc7a08 --- /dev/null +++ b/assets/eip-eosis/implementation/IAssembleContract.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.0; + +/** + * @title IAssembleContract + */ +interface IAssembleContract { + /** + * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @param attrs_ Array of corresponding property values sequentially for each connected contract. + * @return A complete SVG image in the form of a String. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * It should be noted that the order in which the asset storage contract is registered must be carefully observed. + */ + function getImage(uint256[] memory attrs_) external view returns (string memory); + + /** + * @notice Returns the count of connected Asset Storages. + * @return Count of Asset Storage. + * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. + */ + function getStorageCount() external view returns (uint256); +} \ No newline at end of file diff --git a/assets/eip-eosis/implementation/IStorageContract.sol b/assets/eip-eosis/implementation/IStorageContract.sol new file mode 100644 index 00000000000000..c87970b2a24640 --- /dev/null +++ b/assets/eip-eosis/implementation/IStorageContract.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.0; + +/** + * @title IStorageContract + * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. + * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. + */ +interface IStorageContract { + /** + * @notice Returns the SVG image tag corresponding to `assetId_`. + * @param assetId_ Asset ID + * @return A SVG image tag of type String. + */ + function getAsset(uint256 assetId_) external view returns (string memory); +} \ No newline at end of file diff --git a/assets/eip-eosis/implementation/PropertyContract.sol b/assets/eip-eosis/implementation/PropertyContract.sol new file mode 100644 index 00000000000000..4c4eebf1e68c28 --- /dev/null +++ b/assets/eip-eosis/implementation/PropertyContract.sol @@ -0,0 +1,54 @@ +pragma solidity ^0.8.0; + +import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import {IAssembleContract} from "./IAssembleContract.sol"; + +/** + * @title PropertyContract + * @notice A contract that stores property values. + */ +contract PropertyContract is ERC721 { + /** + * @notice A variable that stores the object of `AssembleContract`. + */ + IAssembleContract public assembleContract; + + // Storing property values corresponding to each number of storage. (tokenId -> attr[]) + mapping(uint256 => uint256[]) private _attrs; + + /** + * @dev `name_` and `symbol_` are passed to ERC-721, and in case of `assembleContractAddr_`, the `setAssembleContract` function is used. + */ + constructor(string memory name_, string memory symbol_, address assembleContractAddr_) ERC721(name_, symbol_) { + setAssembleContract(assembleContractAddr_); + } + + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + + /// @dev Implement the property value setting logic. + + _attrs[tokenId_].push(newValue); + } + } +} \ No newline at end of file diff --git a/assets/eip-eosis/implementation/StorageContract.sol b/assets/eip-eosis/implementation/StorageContract.sol new file mode 100644 index 00000000000000..28fd341bc99280 --- /dev/null +++ b/assets/eip-eosis/implementation/StorageContract.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.8.0; + +import {IStorageContract} from "./IStorageContract.sol"; + +/** + * @title StorageContract + * @notice A contract that stores SVG image tags. + * @dev See {IStorageContract} + */ +contract StorageContract is IStorageContract { + + // Asset List + mapping(uint256 => string) private _assetList; + + /** + * @dev Write the values of assets (SVG image tags) to be stored in this `StorageContract`. + */ + constructor () { + // Setting Assets such as _assetList[1234] = " Date: Wed, 23 Feb 2022 17:11:51 +0900 Subject: [PATCH 02/12] eip-eosis => eip-4841 --- EIPS/{eip-eosis.md => eip-4841.md} | 6 +++--- assets/{eip-eosis => eip-4841}/StructureDiagram.jpg | Bin .../eip-eosis_KR.md => eip-4841/eip-4841_KR.md} | 0 .../implementation/AssembleContract.sol | 0 .../implementation/IAssembleContract.sol | 0 .../implementation/IStorageContract.sol | 0 .../implementation/PropertyContract.sol | 0 .../implementation/StorageContract.sol | 0 8 files changed, 3 insertions(+), 3 deletions(-) rename EIPS/{eip-eosis.md => eip-4841.md} (99%) rename assets/{eip-eosis => eip-4841}/StructureDiagram.jpg (100%) rename assets/{eip-eosis/eip-eosis_KR.md => eip-4841/eip-4841_KR.md} (100%) rename assets/{eip-eosis => eip-4841}/implementation/AssembleContract.sol (100%) rename assets/{eip-eosis => eip-4841}/implementation/IAssembleContract.sol (100%) rename assets/{eip-eosis => eip-4841}/implementation/IStorageContract.sol (100%) rename assets/{eip-eosis => eip-4841}/implementation/PropertyContract.sol (100%) rename assets/{eip-eosis => eip-4841}/implementation/StorageContract.sol (100%) diff --git a/EIPS/eip-eosis.md b/EIPS/eip-4841.md similarity index 99% rename from EIPS/eip-eosis.md rename to EIPS/eip-4841.md index acdc15d066ee59..003066ce4588d8 100644 --- a/EIPS/eip-eosis.md +++ b/EIPS/eip-4841.md @@ -1,5 +1,5 @@ --- -eip: +eip: 4841 title: Expandable Onchain SVG Images Storage Structure description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. author: KyungEun Kim , Soohan Park <725psh@gmail.com> @@ -24,7 +24,7 @@ It is a structure designed to store SVG images with a larger capacity by distrib The structure presented by this EIP consists of a total of three layers as shown below. -![StructureDiagram.jpg](../assets/eip-eosis/StructureDiagram.jpg) +![StructureDiagram.jpg](../assets/eip-4841/StructureDiagram.jpg) > **Storage Layer ─** A contract layer that stores distributed SVG images by tags. > **Assemble Layer ─** A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. @@ -412,7 +412,7 @@ contract StorageContract is IStorageContract { } ``` -You can also view these files in [here](../assets/eip-eosis/implementation/). +You can also view these files in [here](../assets/eip-4841/implementation/). diff --git a/assets/eip-eosis/StructureDiagram.jpg b/assets/eip-4841/StructureDiagram.jpg similarity index 100% rename from assets/eip-eosis/StructureDiagram.jpg rename to assets/eip-4841/StructureDiagram.jpg diff --git a/assets/eip-eosis/eip-eosis_KR.md b/assets/eip-4841/eip-4841_KR.md similarity index 100% rename from assets/eip-eosis/eip-eosis_KR.md rename to assets/eip-4841/eip-4841_KR.md diff --git a/assets/eip-eosis/implementation/AssembleContract.sol b/assets/eip-4841/implementation/AssembleContract.sol similarity index 100% rename from assets/eip-eosis/implementation/AssembleContract.sol rename to assets/eip-4841/implementation/AssembleContract.sol diff --git a/assets/eip-eosis/implementation/IAssembleContract.sol b/assets/eip-4841/implementation/IAssembleContract.sol similarity index 100% rename from assets/eip-eosis/implementation/IAssembleContract.sol rename to assets/eip-4841/implementation/IAssembleContract.sol diff --git a/assets/eip-eosis/implementation/IStorageContract.sol b/assets/eip-4841/implementation/IStorageContract.sol similarity index 100% rename from assets/eip-eosis/implementation/IStorageContract.sol rename to assets/eip-4841/implementation/IStorageContract.sol diff --git a/assets/eip-eosis/implementation/PropertyContract.sol b/assets/eip-4841/implementation/PropertyContract.sol similarity index 100% rename from assets/eip-eosis/implementation/PropertyContract.sol rename to assets/eip-4841/implementation/PropertyContract.sol diff --git a/assets/eip-eosis/implementation/StorageContract.sol b/assets/eip-4841/implementation/StorageContract.sol similarity index 100% rename from assets/eip-eosis/implementation/StorageContract.sol rename to assets/eip-4841/implementation/StorageContract.sol From 8923b69869dfeeb3482c19ce60d398499825cf7f Mon Sep 17 00:00:00 2001 From: SoohanPark <725psh@gmail.com> Date: Wed, 23 Feb 2022 17:30:32 +0900 Subject: [PATCH 03/12] Add Author's Github ID @Soohan-Park --- EIPS/eip-4841.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index 003066ce4588d8..c047e860f79860 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -2,7 +2,7 @@ eip: 4841 title: Expandable Onchain SVG Images Storage Structure description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. -author: KyungEun Kim , Soohan Park <725psh@gmail.com> +author: KyungEun Kim , Soohan Park <725psh@gmail.com>, Soohan Park (@Soohan-Park) discussions-to: https://ethereum-magicians.org/t/expandable-onchain-svg-images-storage-structure/8330 status: Draft type: Standards Track From 53b3e4580498596570fd8fed7e0b5dd751729f52 Mon Sep 17 00:00:00 2001 From: SoohanPark <725psh@gmail.com> Date: Thu, 24 Feb 2022 09:20:41 +0900 Subject: [PATCH 04/12] Change link of `discussions-to` --- EIPS/eip-4841.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index c047e860f79860..e46d04fbdfc6e9 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -3,7 +3,7 @@ eip: 4841 title: Expandable Onchain SVG Images Storage Structure description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. author: KyungEun Kim , Soohan Park <725psh@gmail.com>, Soohan Park (@Soohan-Park) -discussions-to: https://ethereum-magicians.org/t/expandable-onchain-svg-images-storage-structure/8330 +discussions-to: https://ethereum-magicians.org/t/eip-4841-expandable-onchain-svg-images-storage-structure/8410 status: Draft type: Standards Track category: ERC From 501cf92b2f8fd7d61203e64a2fb5808107765680 Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Thu, 24 Feb 2022 17:49:44 +0900 Subject: [PATCH 05/12] Delete Simple Summary & Change to relative path --- EIPS/eip-4841.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index e46d04fbdfc6e9..ecba0e26d3db83 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -12,11 +12,6 @@ created: 2022-02-23 -## Simple Summary -It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. - - - ## Abstract This standard proposal is a Expandable Onchain SVG Images Storage Structure Model on the Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. @@ -37,7 +32,7 @@ Also, you can configure the Onchain NFT Images Storage by adjusting the Assemble * A storage with expandability by allowing additional deployment on Storage Layer's contracts * A storage with immutability after initial deployment -Additionally, this standard proposal focuses on, but is not limited to, compatibility with the [EIP-721](/EIPS/eip-721.md) standard. +Additionally, this standard proposal focuses on, but is not limited to, compatibility with the [EIP-721](./eip-721.md) standard. From 12ee7984588eeb5fd126b8abe44bbcc2de1183af Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Fri, 4 Mar 2022 21:38:21 +0900 Subject: [PATCH 06/12] Reduce the number of title characters - Remove 'Structure' --- EIPS/eip-4841.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index ecba0e26d3db83..44bb51394e0e51 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -1,6 +1,6 @@ --- eip: 4841 -title: Expandable Onchain SVG Images Storage Structure +title: Expandable Onchain SVG Images Storage description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. author: KyungEun Kim , Soohan Park <725psh@gmail.com>, Soohan Park (@Soohan-Park) discussions-to: https://ethereum-magicians.org/t/eip-4841-expandable-onchain-svg-images-storage-structure/8410 From 8530689ac30f822f25a3ffdb4ea81d4e974d4847 Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Fri, 4 Mar 2022 21:38:41 +0900 Subject: [PATCH 07/12] Remove EIP KR ver. file --- assets/eip-4841/eip-4841_KR.md | 429 --------------------------------- 1 file changed, 429 deletions(-) delete mode 100644 assets/eip-4841/eip-4841_KR.md diff --git a/assets/eip-4841/eip-4841_KR.md b/assets/eip-4841/eip-4841_KR.md deleted file mode 100644 index 2827a878f2b1d1..00000000000000 --- a/assets/eip-4841/eip-4841_KR.md +++ /dev/null @@ -1,429 +0,0 @@ ---- -eip: -title: Expandable Onchain SVG Images Storage Structure -description: It is a Expandable Onchain SVG Images Storage Structure Model on the Ethereum. -author: , Soohan Park <725psh@gmail.com> -discussions-to: https://ethereum-magicians.org/t/expandable-onchain-svg-images-storage-structure/8330 -status: Draft -type: Standards Track -category: ERC -created: -requires: 721 ---- - -## Simple Summary -이더리움에서 확장 가능한 온체인 SVG 이미지 저장 구조 모델입니다. - - - -## Abstract -이 표준 제안은 이더리움 온체인 상에 이미지를 영구적으로 보존하고 변조를 방지하며, 나아가 더 큰 용량의 이미지를 저장할 수 있고 확장 가능한 SVG 이미지 분산 저장 구조 모델입니다. - -SVG 이미지를 태그 단위로 분산 저장하여 온체인 상에서 더 큰 용량의 SVG 이미지를 저장할 수 있도록 설계한 구조입니다. - -이 EIP에서 제시하는 구조는 아래와 같이 총 3개의 레이어로 구성되어 있습니다. - -![StructureDiagram.jpg](./StructureDiagram.jpg) - -> **Storage Layer ─** SVG 이미지를 태그별로 분산 저장하는 컨트랙트 레이어. -> **Assemble Layer ─** 'Storage Layer Contract'에 저장되어 있는 태그들을 조합하여 SVG 이미지를 생성하는 컨트랙트 레이어. -> **Property Layer ─** 어떠한 SVG 태그를 사용할 지, 그에 대한 속성값이 저장되어 있는 컨트랙트 레이어. - -위 3개의 레이어별 컨트랙트들이 서로 상호작용하며 큰 크기의 SVG 이미지를 유연하게 저장하고 활용할 수 있도록 설계하였습니다. - -또한, 아래와 같이 Assemble Layer의 컨트랙트를 조정하여 Onchain NFT Images Storage를 구성할 수 있습니다. - -- A storage with expandability by allowing additional deployment on Storage Layer's contracts -- A storage with immutability after initial deployment - -추가적으로, 이 표준 제안은 EIP-721 표준과의 호환성에 중점을 두고 있지만, 이에 국한되지는 않습니다. - - - -## Motivation -대다수의 NFT 프로젝트들은 자신들의 NFT 메타데이터와 컨텐츠들을 이더리움 네트워크가 아닌 중앙 집중식 서버에 보관하고 있습니다. 이 방법은 NFT의 컨텐츠를 표시하는 가장 저렴하고 손쉬운 방법이지만, NFT의 메타데이터나 컨텐츠가 손상되거나 손실될 위험이 존재합니다. 또한, IPFS의 경우에도 컨텐츠의 변조를 방지할 수는 있지만, 컨텐츠를 저장하고 있는 노드가 없는 경우 컨텐츠가 손실될 수도 있습니다. - -이 문제를 해결하기 위해 대부분 NFT메타데이터를 이더리움 온체인으로 저장합니다. 그러나 한 컨트랙트에 배포 가능한 최대 크기는 24KB이기 때문에 단순한 도형 ─원이나 사각형─ 으로 표현할 수 밖에 없습니다. - -우리는 고품질의 NFT 메타데이터를 생성하고 소유하기 위해 NFT 메타데이터를 저장하는 보다 안전한 방법인 이 모델을 제안합니다. - - - -## Specification -The key words “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. - -이 EIP에서 제안하는 모델의 3개의 레이어에 대해 각각 구체적으로 설명하려 합니다. - -기능과 역할에 따라 레이어를 구분 짓고 이를 각기 다른 컨트랙트로 배포한 뒤, 이 컨트랙트들을 논리적으로 연결하여 하나의 큰 저장 공간을 갖는 컨트랙트처럼 활용되도록 구성하였습니다. - -### Storage Layer - -SVG 이미지를 태그별로 분산하여 저장하는 컨트랙트 레이어입니다. - -```solidity -pragma solidity ^0.8.0; - -/** - * @title IStorageContract - * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. - * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. - * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. - */ -interface IStorageContract { - /** - * @notice Returns the SVG image tag corresponding to `assetId_`. - * @param assetId_ Asset ID - * @return A SVG image tag of type String. - */ - function getAsset(uint256 assetId_) external view returns (string memory); -} -``` - -이 Storage Layer's contracts들은 배포된 이후 오직 저장된 SVG 이미지 태그를 Assemble Layer로 전달해주는 역할만 수행할 수 있습니다. - -우리는 데이터 오염을 고려해야하기 때문에 `setAsset`과 같은 기능을 구현하지 않았습니다. **(SHOULD)** 따라서, SVG 이미지 태그를 등록하는 것은 컨트랙트를 배포할 때만 가능하며, 추후 변경이 필요한 경우 새로운 컨트랙트를 배포하는 것을 추천합니다. **(RECOMMENDED)** - -### Assemble Layer - -저장되어 있는 태그들을 조합하여 이미지를 생성하는 미들맨 역할의 컨트랙트 레이어입니다. - -```solidity -pragma solidity ^0.8.0; - -/** - * @title IAssembleContract - */ -interface IAssembleContract { - /** - * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. - * @param attrs_ Array of corresponding property values sequentially for each connected contract. - * @return A complete SVG image in the form of a String. - * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. - * It should be noted that the order in which the asset storage contract is registered must be carefully observed. - */ - function getImage(uint256[] memory attrs_) external view returns (string memory); - - /** - * @notice Returns the count of connected Asset Storages. - * @return Count of Asset Storage. - * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. - */ - function getStorageCount() external view returns (uint256); -} -``` - -Assemble Layer의 컨트랙트에 새로운 Storage Layer 컨트랙트를 추가하는 함수인 `addStorage(address)` 기능은 인터페이스에 포함되어 있지 않습니다. 왜냐하면, 우리는 각 레이어가 서로 격리되어 있기를 원하기 때문입니다. - -e.g. `addStorage(address)` 함수는 다음과 같이 구현할 수 있습니다. - -```solidity -/** - * @param storageAddr_ Address of `StorageContract`. - * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. - */ -function addStorage(address storageAddr_) public virtual returns (uint256) { - _assets.push(AssetStorage({ - addr: storageAddr_, - stock: IStorageContract(storageAddr_) - })); - return _assets.length-1; // index -} -``` - -### Property Layer - -어떠한 SVG 태그 값들을 가져올 지에 대한 속성 값이 저장되어 있는 컨트랙트 레이어입니다. - -사용자는 Property Layer를 통해 저장된 이미지에 접근할 수 있으며, EIP-721 혹은 이 외의 규격들에 대한 상속 역시 이 레이어에서 하게 됩니다. - -아래의 함수들은 Property Layer's contract에서 구현해야 할 주요 함수들에 대한 설명입니다. - -- `getImage(uint256)`: 저장한 SVG 이미지를 가져오는 함수입니다. `tokenId_`에 해당하는 속성값들을 가져와 Assemble Layer 인터페이스의 getImage를 호출합니다. - - ```solidity - /** - * @dev See {IAssembleContract-getImage} - */ - function getImage(uint256 tokenId_) public view virtual returns (string memory) { - return assembleContract.getImage(_attrs[tokenId_]); - } - ``` - -- `setAssembleContract(address)`: Assemble Layer's Contract를 설정하는 함수입니다. 만약, 최초 배포 후 변경이 불가능한 immutable storage 로 활용하고자 한다면, 해당 함수를 제거하면 됩니다. - - ```solidity - /** - * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. - * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. - */ - function setAssembleContract(address newAssembleContractAddr_) public virtual { - assembleContract = IAssembleContract(newAssembleContractAddr_); - } - ``` - -- `_setAttr(uint256)`: 어떠한 SVG 이미지 태그를 불러올 것인지 속성값을 설정해주는 함수입니다. 어떻게 속성값을 설정해줄지에 대한 로직은 반드시 활용하고자 하는 방향에 맞춰 별도로 구현해주어야 합니다. (**MUST**) - - ```solidity - /** - * @param tokenId_ The token ID for which you want to set the attribute value. - * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. - */ - function _setAttr(uint256 tokenId_) internal virtual { - for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { - uint256 newValue = 0; - - /// @dev Implement the property value setting logic. - - _attrs[tokenId_].push(newValue); - } - } - ``` - - - -## Rationale - -### Large Capacity Storage - -우리가 콘텐츠를 영구적이고 변조 불가능하게 보관하는 가장 좋은 방법은 변조 혹은 유실될 수 있는 중앙 집중식 서버나 IPFS가 아닌, 온체인 상에 저장하는 것입니다. SVG 형식과 같이 콘텐츠의 용량을 줄이기 위해 다양한 확장들이 나왔지만, 대부분의 콘텐츠들은 아직 수 MB 이상의 크기를 가지고 있습니다. 이 EIP를 통해 수십 KB에서 수 MB에 이르는 큰 크기의 SVG 이미지들을 온체인 상에 안전하게 저장할 수 있는 하나의 솔루션을 제공하고자 합니다. - -### Cost Efficiency - -이 EIP에서 제안하는 방식은 많은 수의 컨트랙트 배포를 필요로 합니다. 따라서, 저장하고자 하는 SVG 이미지의 수가 적거나, 하나의 컨트랙트에 모두 포함시킬 수 있을만큼 크기가 작은 경우에는 이 EIP를 활용하는 것을 다시 한 번 검토해볼 필요가 있습니다. - -### Expandable - -이 EIP에서는 **데이터 오염을 방지하기 위해** 아래와 같이 **확장 가능성**에 대해 약간의 제한을 두었습니다. - -- Storage Layer에 속하는 컨트랙트들을 **WRITE ONLY ONCE**로 설정하였습니다. SVG 이미지 태그들을 직접 저장하고 있는 곳이므로, 데이터 오염을 방지하기 위해 첫 배포시에만 값을 설정할 수 있도록 하였습니다. 만약, 값에 대한 수정이 필요한 경우 새로운 컨트랙트를 배포해야 합니다. -- Assemble Layer와 Storage Layer를 서로 연결할 때는 **APPEND ONLY**로 설정하였습니다. 확장성은 유지하되, 기존에 연결된 컨트랙트들은 변경되지 않도록 설계하여 데이터 오염을 최소화하고자 하였습니다. - - - -## Backwards Compatibility -There are no backward compatibility issues. - - - -## Reference Implementation - -### PropertyContract.sol -```solidity -pragma solidity ^0.8.0; - -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import {IAssembleContract} from "./IAssembleContract.sol"; - -/** - * @title PropertyContract - * @notice A contract that stores property values. - */ -contract PropertyContract is ERC721 { - /** - * @notice A variable that stores the object of `AssembleContract`. - */ - IAssembleContract public assembleContract; - - // Storing property values corresponding to each number of storage. (tokenId -> attr[]) - mapping(uint256 => uint256[]) private _attrs; - - /** - * @dev `name_` and `symbol_` are passed to ERC-721, and in case of `assembleContractAddr_`, the `setAssembleContract` function is used. - */ - constructor(string memory name_, string memory symbol_, address assembleContractAddr_) ERC721(name_, symbol_) { - setAssembleContract(assembleContractAddr_); - } - - /** - * @dev See {IAssembleContract-getImage} - */ - function getImage(uint256 tokenId_) public view virtual returns (string memory) { - return assembleContract.getImage(_attrs[tokenId_]); - } - - /** - * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. - * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. - */ - function setAssembleContract(address newAssembleContractAddr_) public virtual { - assembleContract = IAssembleContract(newAssembleContractAddr_); - } - - /** - * @param tokenId_ The token ID for which you want to set the attribute value. - * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. - */ - function _setAttr(uint256 tokenId_) internal virtual { - for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { - uint256 newValue = 0; - - /// @dev Implement the property value setting logic. - - _attrs[tokenId_].push(newValue); - } - } -} -``` - -### IAssembleContract.sol -```solidity -pragma solidity ^0.8.0; - -/** - * @title IAssembleContract - */ -interface IAssembleContract { - /** - * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. - * @param attrs_ Array of corresponding property values sequentially for each connected contract. - * @return A complete SVG image in the form of a String. - * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. - * It should be noted that the order in which the asset storage contract is registered must be carefully observed. - */ - function getImage(uint256[] memory attrs_) external view returns (string memory); - - /** - * @notice Returns the count of connected Asset Storages. - * @return Count of Asset Storage. - * @dev Instead of storing the count of storage separately in `PropertyContract`, get the value through this function and use it. - */ - function getStorageCount() external view returns (uint256); -} -``` - -### AssembleContract.sol -```solidity -pragma solidity ^0.8.0; - -import {IAssembleContract} from "./IAssembleContract.sol"; -import {IStorageContract} from "./IStorageContract.sol"; - -/** - * @title AssembleContract - * @notice A contract that assembles SVG images. - */ -contract AssembleContract is IAssembleContract { - - /** - * @dev Asset storage structure. Stores the contract address value and the corresponding object. - */ - struct AssetStorage { - address addr; - IStorageContract stock; - } - - AssetStorage[] private _assets; - - /** - * @dev Register address values of `StorageContract`. Pay attention to the order when registering. - */ - constructor (address[] memory assetStorageAddrList_) { - for (uint256 i=0; i < assetStorageAddrList_.length; i++) { - addStorage(assetStorageAddrList_[i]); - } - } - - /** - * @dev See {IAssembleContract-getImage} - */ - function getImage(uint256[] memory attrs_) external view virtual override returns (string memory) { - string memory imageString = ""; - - imageString = string(abi.encodePacked(imageString, "")); - - for (uint256 i=0; i < attrs_.length; i++) { - imageString = string( - abi.encodePacked( - imageString, - _assets[i].stock.getAsset(attrs_[i]) - ) - ); - } - - imageString = string(abi.encodePacked(imageString, '')); - - return imageString; - } - - /** - * See {IAssembleContract-getStorageCount} - */ - function getStorageCount() external view virtual override returns (uint256) { - return _assets.length; - } - - /** - * @param storageAddr_ Address of `StorageContract`. - * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. - */ - function addStorage(address storageAddr_) public virtual returns (uint256) { - _assets.push(AssetStorage({ - addr: storageAddr_, - stock: IStorageContract(storageAddr_) - })); - return _assets.length-1; // index - } -} -``` - -### IStorageContract.sol -```solidity -pragma solidity ^0.8.0; - -/** - * @title IStorageContract - * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. - * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. - * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. - */ -interface IStorageContract { - /** - * @notice Returns the SVG image tag corresponding to `assetId_`. - * @param assetId_ Asset ID - * @return A SVG image tag of type String. - */ - function getAsset(uint256 assetId_) external view returns (string memory); -} -``` - -### StorageContract.sol -```solidity -pragma solidity ^0.8.0; - -import {IStorageContract} from "./IStorageContract.sol"; - -/** - * @title StorageContract - * @notice A contract that stores SVG image tags. - * @dev See {IStorageContract} - */ -contract StorageContract is IStorageContract { - - // Asset List - mapping(uint256 => string) private _assetList; - - /** - * @dev Write the values of assets (SVG image tags) to be stored in this `StorageContract`. - */ - constructor () { - // Setting Assets such as _assetList[1234] = " Date: Fri, 4 Mar 2022 21:49:35 +0900 Subject: [PATCH 08/12] Remove unneeded link --- EIPS/eip-4841.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index 44bb51394e0e51..ae011c923cfe68 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -407,8 +407,6 @@ contract StorageContract is IStorageContract { } ``` -You can also view these files in [here](../assets/eip-4841/implementation/). - ## Security Considerations From e888b69934b21d4e7ad51997199a210fd50202b4 Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Sat, 19 Mar 2022 17:26:54 +0900 Subject: [PATCH 09/12] Remove extra spaces --- EIPS/eip-4841.md | 114 ++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index ae011c923cfe68..dc85984c27993d 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -10,10 +10,9 @@ category: ERC created: 2022-02-23 --- - - ## Abstract -This standard proposal is a Expandable Onchain SVG Images Storage Structure Model on the Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. + +This standard proposal is a Expandable Onchain SVG Images Storage on the Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. It is a structure designed to store SVG images with a larger capacity by distributed SVG images in units of tags on Ethereum. @@ -23,29 +22,27 @@ The structure presented by this EIP consists of a total of three layers as shown > **Storage Layer ─** A contract layer that stores distributed SVG images by tags. > **Assemble Layer ─** A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. -> **Property Layer ─** A contract layer that stores the attribute values for which SVG tag to use. +> **Property Layer ─** A contract layer that stores the attribute values for which SVG tag to use. It is designed to flexibly store and utilize larger capacity SVG images by interacting with the above three layer-by-layer contracts each other. Also, you can configure the Onchain NFT Images Storage by adjusting the Assemble Layer's contract like below. -* A storage with expandability by allowing additional deployment on Storage Layer's contracts -* A storage with immutability after initial deployment +- A storage with expandability by allowing additional deployment on Storage Layer's contracts +- A storage with immutability after initial deployment Additionally, this standard proposal focuses on, but is not limited to, compatibility with the [EIP-721](./eip-721.md) standard. - - ## Motivation + Most NFT projects store their NFT metadata on a centralized server rather than on the Ethereum. Although this method is the cheapest and easiest way to store and display the content of the NFT, there is a risk of corruption or loss of the NFT's metadata. In addition, even in the case of IPFS, tampering of contents can be prevented, but contents could be lost if there is no node storing the contents. To solve this problem, most NFT metadata is stored on Ethereum. However, it can only be expressed as a simple shape such as a circle or a rectangle, since one contract can be distributed 24KB size for maximum. -We propose this model *─ a more secure way to store NFT metadata ─* to create and own high-quality of NFT metadata. - - +We propose this model _─ a more secure way to store NFT metadata ─_ to create and own high-quality of NFT metadata. ## Specification + The key words “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. We would like to explain each of the three layers of the proposed model in detail. @@ -53,6 +50,7 @@ We would like to explain each of the three layers of the proposed model in detai After classifying layers according to functions and roles and distributing them to different contracts, these contracts are logically connected to be used like a contract with one large storage space. ### Storage Layer + A contract layer that stores distributed SVG images by tags. ```solidity @@ -79,12 +77,13 @@ After these Storage Layer's contracts are deployed, they can only perform the ro Since we **SHOULD** have to consider data contamination, we didn't implement the function of `setAsset`. Therefore, registering SVG image tags is only possible when deploying a contract, and it is **RECOMMENDED** to deploy a new contract if changes are required in the future. ### Assemble Layer -A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. + +A contract layer that creates SVG images by combining tags stored in the Storage Layer's contract. ```solidity pragma solidity ^0.8.0; -/** +/** * @title IAssembleContract */ interface IAssembleContract { @@ -125,7 +124,8 @@ function addStorage(address storageAddr_) public virtual returns (uint256) { ``` ### Property Layer -A contract layer that stores the attribute values for which SVG tag to use. + +A contract layer that stores the attribute values for which SVG tag to use. The user can access the saved image through the Property Layer, and inheritance of EIP-721 or other standards is also performed in this layer. @@ -133,70 +133,70 @@ The following functions are descriptions of the main functions to be implemented - `getImage(uint256)`: This function gets the saved SVG image. Get the property values corresponding to `tokenId_` and call `getImage` of the Assemble Layer interface. - ```solidity - /** - * @dev See {IAssembleContract-getImage} - */ - function getImage(uint256 tokenId_) public view virtual returns (string memory) { - return assembleContract.getImage(_attrs[tokenId_]); - } - ``` + ```solidity + /** + * @dev See {IAssembleContract-getImage} + */ + function getImage(uint256 tokenId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[tokenId_]); + } + ``` - `setAssembleContract(address)`: This function sets the Assemble Layer's Contract. If you want to use it as an Immutable Storage that cannot be changed after the initial deployment, you can remove the function. - ```solidity - /** - * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. - * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. - */ - function setAssembleContract(address newAssembleContractAddr_) public virtual { - assembleContract = IAssembleContract(newAssembleContractAddr_); - } - ``` + ```solidity + /** + * @param newAssembleContractAddr_ Address value of `AssembleContract` to be changed. + * @dev If later changes or extensions are unnecessary, write directly to `constructor` without implementing the function. + */ + function setAssembleContract(address newAssembleContractAddr_) public virtual { + assembleContract = IAssembleContract(newAssembleContractAddr_); + } + ``` - `_setAttr(uint256)`: This is a function that sets the attribute value of which SVG image tag to load. The logic for how to set the property value **MUST** be implemented separately according to the direction to be used. - ```solidity - /** - * @param tokenId_ The token ID for which you want to set the attribute value. - * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. - */ - function _setAttr(uint256 tokenId_) internal virtual { - for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { - uint256 newValue = 0; - - /// @dev Implement the property value setting logic. - - _attrs[tokenId_].push(newValue); - } - } - ``` + ```solidity + /** + * @param tokenId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + */ + function _setAttr(uint256 tokenId_) internal virtual { + for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + uint256 newValue = 0; + /// @dev Implement the property value setting logic. + _attrs[tokenId_].push(newValue); + } + } + ``` ## Rationale ### Large Capacity Storage + The best way for us to keep our content permanent and tamper-proof is to store it on Ethereum, rather than on centralized servers or IPFS, where it can be tampered with or lost. Like the SVG format, various extensions have come out to reduce the size of the content, but most of the content still has a size of several MB or more. Through this EIP, we would like to provide a solution that can safely store SVG images in sizes ranging from tens of KB to several MB on Ethereum. ### Cost Efficiency + The protocol proposed by this EIP requires the deployment of a large number of contracts. Therefore, it is necessary to reconsider using this EIP if the number of SVG images you want to save is small or the size is small enough to include them all in one contract. ### Expandable + In this EIP, to prevent data contamination, we have placed some restrictions on **EXPANDABLE** as shown below. - Storage Layer's contracts are set to **be written only once**. Since it is a place where SVG image tags are stored directly, values can only be set at the first deployment to prevent data contamination. If you need to modify the value, you need to deploy a new contract. - When connecting the Storage Layer to the Assemble Layer, it is set to **be appended only**. By designing existing connected contracts not to change, we tried to minimize data contamination while maintaining scalability. - - ## Backwards Compatibility -There are no backward compatibility issues. - +There are no backward compatibility issues. ## Reference Implementation + ### PropertyContract.sol + ```solidity pragma solidity ^0.8.0; @@ -248,17 +248,18 @@ contract PropertyContract is ERC721 { /// @dev Implement the property value setting logic. - _attrs[tokenId_].push(newValue); + _attrs[tokenId_].push(newValue); } } } ``` ### IAssembleContract.sol + ```solidity pragma solidity ^0.8.0; -/** +/** * @title IAssembleContract */ interface IAssembleContract { @@ -281,6 +282,7 @@ interface IAssembleContract { ``` ### AssembleContract.sol + ```solidity pragma solidity ^0.8.0; @@ -356,6 +358,7 @@ contract AssembleContract is IAssembleContract { ``` ### IStorageContract.sol + ```solidity pragma solidity ^0.8.0; @@ -376,6 +379,7 @@ interface IStorageContract { ``` ### StorageContract.sol + ```solidity pragma solidity ^0.8.0; @@ -407,12 +411,10 @@ contract StorageContract is IStorageContract { } ``` - - ## Security Considerations -There are no known security considerations for this EIP. - +There are no known security considerations for this EIP. ## Copyright + Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From ac87a4111f07269131984e55333e119315ac4b09 Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Sat, 19 Mar 2022 17:33:44 +0900 Subject: [PATCH 10/12] Re-write 'description' in Preamble & remove unnecesary capitalizations. --- EIPS/eip-4841.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index dc85984c27993d..1e10406bb58950 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -1,7 +1,7 @@ --- eip: 4841 title: Expandable Onchain SVG Images Storage -description: It is a Expandable Onchain SVG Images Storage Structure Model on Ethereum. +description: Expandable SVG images storage model for store larger-capacity images on Ethereum. author: KyungEun Kim , Soohan Park <725psh@gmail.com>, Soohan Park (@Soohan-Park) discussions-to: https://ethereum-magicians.org/t/eip-4841-expandable-onchain-svg-images-storage-structure/8410 status: Draft @@ -26,7 +26,7 @@ The structure presented by this EIP consists of a total of three layers as shown It is designed to flexibly store and utilize larger capacity SVG images by interacting with the above three layer-by-layer contracts each other. -Also, you can configure the Onchain NFT Images Storage by adjusting the Assemble Layer's contract like below. +Also, you can configure the onchain NFT images storage by adjusting the Assemble Layer's contract like below. - A storage with expandability by allowing additional deployment on Storage Layer's contracts - A storage with immutability after initial deployment From ce063bd39c5df8ca1563f2ffcbfd1f057199cc45 Mon Sep 17 00:00:00 2001 From: Soohan Park <725psh@gmail.com> Date: Sun, 20 Mar 2022 00:05:19 +0900 Subject: [PATCH 11/12] Check grammar --- EIPS/eip-4841.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/EIPS/eip-4841.md b/EIPS/eip-4841.md index 1e10406bb58950..ce9ffcd7917c9b 100644 --- a/EIPS/eip-4841.md +++ b/EIPS/eip-4841.md @@ -1,7 +1,7 @@ --- eip: 4841 title: Expandable Onchain SVG Images Storage -description: Expandable SVG images storage model for store larger-capacity images on Ethereum. +description: Expandable SVG images storage model for storing larger-capacity images on Ethereum. author: KyungEun Kim , Soohan Park <725psh@gmail.com>, Soohan Park (@Soohan-Park) discussions-to: https://ethereum-magicians.org/t/eip-4841-expandable-onchain-svg-images-storage-structure/8410 status: Draft @@ -12,9 +12,9 @@ created: 2022-02-23 ## Abstract -This standard proposal is a Expandable Onchain SVG Images Storage on the Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. +This standard proposal is an Expandable Onchain SVG Images Storage on Ethereum that permanently preserves images and prevents tampering, and can store larger-capacity images furthermore. -It is a structure designed to store SVG images with a larger capacity by distributed SVG images in units of tags on Ethereum. +It is a structure designed to store SVG images with a larger capacity by distributing SVG images in units of tags on Ethereum. The structure presented by this EIP consists of a total of three layers as shown below. @@ -26,20 +26,20 @@ The structure presented by this EIP consists of a total of three layers as shown It is designed to flexibly store and utilize larger capacity SVG images by interacting with the above three layer-by-layer contracts each other. -Also, you can configure the onchain NFT images storage by adjusting the Assemble Layer's contract like below. +Also, you can configure the on-chain NFT images storage by adjusting the Assemble Layer's contract like below. -- A storage with expandability by allowing additional deployment on Storage Layer's contracts -- A storage with immutability after initial deployment +- Storage with expandability by allowing additional deployment on Storage Layer's contracts +- Storage with immutability after initial deployment -Additionally, this standard proposal focuses on, but is not limited to, compatibility with the [EIP-721](./eip-721.md) standard. +Additionally, this standard proposal focuses on but is not limited to compatibility with the [EIP-721](./eip-721.md) standard. ## Motivation -Most NFT projects store their NFT metadata on a centralized server rather than on the Ethereum. Although this method is the cheapest and easiest way to store and display the content of the NFT, there is a risk of corruption or loss of the NFT's metadata. In addition, even in the case of IPFS, tampering of contents can be prevented, but contents could be lost if there is no node storing the contents. +Most NFT projects store their NFT metadata on a centralized server rather than on Ethereum. Although this method is the cheapest and easiest way to store and display the content of the NFT, there is a risk of corruption or loss of the NFT's metadata. In addition, even in the case of IPFS, tampering of contents can be prevented, but contents could be lost if there is no node storing the contents. -To solve this problem, most NFT metadata is stored on Ethereum. However, it can only be expressed as a simple shape such as a circle or a rectangle, since one contract can be distributed 24KB size for maximum. +To solve this problem, most NFT metadata is stored on Ethereum. However, it can only be expressed as a simple shape such as a circle or a rectangle, since one contract can be distributed 24 KB size for maximum. -We propose this model _─ a more secure way to store NFT metadata ─_ to create and own high-quality of NFT metadata. +We propose this model _─ a more secure way to store NFT metadata ─_ to create and own high-quality NFT metadata. ## Specification @@ -47,7 +47,7 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL We would like to explain each of the three layers of the proposed model in detail. -After classifying layers according to functions and roles and distributing them to different contracts, these contracts are logically connected to be used like a contract with one large storage space. +After classifying layers according to functions and roles and distributing them to different contracts, these contracts are logically connected to be used as a contract with one large storage space. ### Storage Layer @@ -91,7 +91,7 @@ interface IAssembleContract { * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. * @param attrs_ Array of corresponding property values sequentially for each connected contract. * @return A complete SVG image in the form of a String. - * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value, and combines it into one image. * It should be noted that the order in which the asset storage contract is registered must be carefully observed. */ function getImage(uint256[] memory attrs_) external view returns (string memory); @@ -105,7 +105,7 @@ interface IAssembleContract { } ``` -The `addStorage(address)` function, add new Storage Layer's contract to Assemble Layer's contract, is not included the interface. Because, we would like to each layer to be isolated from each other. +The `addStorage(address)` function, add new Storage Layer's contract to Assemble Layer's contract, is not included in the interface. Because we would like each layer to be isolated from each other. e.g. The `addStorage(address)` function can be implemented like this: @@ -267,7 +267,7 @@ interface IAssembleContract { * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. * @param attrs_ Array of corresponding property values sequentially for each connected contract. * @return A complete SVG image in the form of a String. - * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value, and combines it into one image. * It should be noted that the order in which the asset storage contract is registered must be carefully observed. */ function getImage(uint256[] memory attrs_) external view returns (string memory); From ced8c0c0b3f3b507387a6b2a65b3c690b5806829 Mon Sep 17 00:00:00 2001 From: SoohanPark <725psh@gmail.com> Date: Tue, 5 Apr 2022 10:37:10 +0900 Subject: [PATCH 12/12] Clean-up & Remove ERC721 inheritance. --- .../implementation/AssembleContract.sol | 6 ++--- .../implementation/IAssembleContract.sol | 6 ++--- .../implementation/IStorageContract.sol | 4 ++-- .../implementation/PropertyContract.sol | 24 ++++++++----------- .../implementation/StorageContract.sol | 6 ++--- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/assets/eip-4841/implementation/AssembleContract.sol b/assets/eip-4841/implementation/AssembleContract.sol index b021699c4e6336..b96435a340a0df 100644 --- a/assets/eip-4841/implementation/AssembleContract.sol +++ b/assets/eip-4841/implementation/AssembleContract.sol @@ -5,7 +5,7 @@ import {IStorageContract} from "./IStorageContract.sol"; /** * @title AssembleContract - * @notice A contract that assembles SVG images. + * @notice A contract that assembles SVG image. */ contract AssembleContract is IAssembleContract { @@ -23,7 +23,7 @@ contract AssembleContract is IAssembleContract { * @dev Register address values of `StorageContract`. Pay attention to the order when registering. */ constructor (address[] memory assetStorageAddrList_) { - for (uint256 i=0; i < assetStorageAddrList_.length; i++) { + for (uint256 i = 0; i < assetStorageAddrList_.length; i++) { addStorage(assetStorageAddrList_[i]); } } @@ -36,7 +36,7 @@ contract AssembleContract is IAssembleContract { imageString = string(abi.encodePacked(imageString, "")); - for (uint256 i=0; i < attrs_.length; i++) { + for (uint256 i = 0; i < attrs_.length; i++) { imageString = string( abi.encodePacked( imageString, diff --git a/assets/eip-4841/implementation/IAssembleContract.sol b/assets/eip-4841/implementation/IAssembleContract.sol index dbb8bf2ccc7a08..285a31b251baf2 100644 --- a/assets/eip-4841/implementation/IAssembleContract.sol +++ b/assets/eip-4841/implementation/IAssembleContract.sol @@ -1,14 +1,14 @@ pragma solidity ^0.8.0; -/** +/** * @title IAssembleContract */ interface IAssembleContract { /** - * @notice For each `StorageContract`, get the corresponding SVG image tag and combine it and return it. + * @notice For each `StorageContract`, get the corresponding XML tag of SVG image and combine it and return it. * @param attrs_ Array of corresponding property values sequentially for each connected contract. * @return A complete SVG image in the form of a String. - * @dev It runs the connected `StorageContract` in the registered order, gets the SVG tag value and combines it into one image. + * @dev It runs the connected `StorageContract` in the registered order, gets XML tags of SVG image and combines it into one image. * It should be noted that the order in which the asset storage contract is registered must be carefully observed. */ function getImage(uint256[] memory attrs_) external view returns (string memory); diff --git a/assets/eip-4841/implementation/IStorageContract.sol b/assets/eip-4841/implementation/IStorageContract.sol index c87970b2a24640..06c61c61515951 100644 --- a/assets/eip-4841/implementation/IStorageContract.sol +++ b/assets/eip-4841/implementation/IStorageContract.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; /** * @title IStorageContract - * @dev A contract that returns stored assets (SVG image tags). `setAsset` is not implemented separately. + * @dev A contract that returns stored assets (XML tags of SVG image). `setAsset` is not implemented separately. * If the `setAsset` function exists, the value of the asset in the contract can be changed, and there is a possibility of data corruption. * Therefore, the value can be set only when the contract is created, and new contract distribution is recommended when changes are required. */ @@ -10,7 +10,7 @@ interface IStorageContract { /** * @notice Returns the SVG image tag corresponding to `assetId_`. * @param assetId_ Asset ID - * @return A SVG image tag of type String. + * @return A XML tag of SVG image. */ function getAsset(uint256 assetId_) external view returns (string memory); } \ No newline at end of file diff --git a/assets/eip-4841/implementation/PropertyContract.sol b/assets/eip-4841/implementation/PropertyContract.sol index 4c4eebf1e68c28..e63dfc265ec0aa 100644 --- a/assets/eip-4841/implementation/PropertyContract.sol +++ b/assets/eip-4841/implementation/PropertyContract.sol @@ -1,33 +1,29 @@ pragma solidity ^0.8.0; -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import {IAssembleContract} from "./IAssembleContract.sol"; /** * @title PropertyContract * @notice A contract that stores property values. */ -contract PropertyContract is ERC721 { +contract PropertyContract { /** * @notice A variable that stores the object of `AssembleContract`. */ IAssembleContract public assembleContract; - // Storing property values corresponding to each number of storage. (tokenId -> attr[]) + // Storing property values corresponding to each number of storage. (imageId -> attr[]) mapping(uint256 => uint256[]) private _attrs; - /** - * @dev `name_` and `symbol_` are passed to ERC-721, and in case of `assembleContractAddr_`, the `setAssembleContract` function is used. - */ - constructor(string memory name_, string memory symbol_, address assembleContractAddr_) ERC721(name_, symbol_) { + constructor(address assembleContractAddr_) { setAssembleContract(assembleContractAddr_); } /** * @dev See {IAssembleContract-getImage} */ - function getImage(uint256 tokenId_) public view virtual returns (string memory) { - return assembleContract.getImage(_attrs[tokenId_]); + function getImage(uint256 imageId_) public view virtual returns (string memory) { + return assembleContract.getImage(_attrs[imageId_]); } /** @@ -39,16 +35,16 @@ contract PropertyContract is ERC721 { } /** - * @param tokenId_ The token ID for which you want to set the attribute value. - * @dev Set the attribute value of the corresponding `tokenId_` sequentially according to the number of asset storage. + * @param imageId_ The token ID for which you want to set the attribute value. + * @dev Set the attribute value of the corresponding `imageId_` sequentially according to the number of asset storage. */ - function _setAttr(uint256 tokenId_) internal virtual { - for (uint256 idx=0; idx < assembleContract.getStorageCount(); idx++) { + function _setAttr(uint256 imageId_) internal virtual { + for (uint256 idx = 0; idx < assembleContract.getStorageCount(); idx++) { uint256 newValue = 0; /// @dev Implement the property value setting logic. - _attrs[tokenId_].push(newValue); + _attrs[imageId_].push(newValue); } } } \ No newline at end of file diff --git a/assets/eip-4841/implementation/StorageContract.sol b/assets/eip-4841/implementation/StorageContract.sol index 28fd341bc99280..26cf761be38bf9 100644 --- a/assets/eip-4841/implementation/StorageContract.sol +++ b/assets/eip-4841/implementation/StorageContract.sol @@ -4,16 +4,16 @@ import {IStorageContract} from "./IStorageContract.sol"; /** * @title StorageContract - * @notice A contract that stores SVG image tags. + * @notice A contract that stores XML tags of SVG image. * @dev See {IStorageContract} */ contract StorageContract is IStorageContract { - // Asset List + // Asset list mapping(uint256 => string) private _assetList; /** - * @dev Write the values of assets (SVG image tags) to be stored in this `StorageContract`. + * @dev Write the values of assets (XML tags of SVG image) to be stored in this `StorageContract`. */ constructor () { // Setting Assets such as _assetList[1234] = "