I added that the caller of render must have rights to use the SVG body in a composed SVG.
Relationships
I haven’t specified how this must be done, so any relationship can be used.
When @AlexPartyPanda and I first started playing with adding accessories to pfps we had a token holder owning both the pfp and the accessory and creating a link between the two.
FancyLoogies introduced us to that the pfp could be the token holder of the accessory and that a linkage could be setup on transfer.
Need to find wording to describe the two tokens, composer and composee feel a bit clumsy.
If I am a contract that’s composing together multiple SVGs, I’ll probably know the position and z-index of where I want to put my child SVGs. What scenarios do you envision where the child SVG knows what z-index to render at, but the composer does not?
Have you considered using the link tag instead of concatenating SVGs on-chain? Would have to more formally describe the URI format, but as a really rough example:
Need to agree if zindex, width and height are required in the EIP or if a render function is sufficient.
A test was adding Party Pandas and Nouns glasses to a Loogie Tank using only a render function. Need to dig in if sizing would help here, as currently the elements go out of the tank.
In PartyPanda there is permissionless linking, a token holder can add a child SVG NFT that is either a background or an accessory by transferring ownership of the child to the composable NFT.
Though this could be implemented in the child SVG NFT to provide a zindex as part of the transfer, so then it wouldn’t need to be part of the standard.
I hadn’t considered using a link tag.
Concatenating SVGs on-chain works today, it allows for the child SVG to change based on state, and when combined with the parent holding the child, enforces ownership.
I assume a new URI format would require a huge effort to get adoption, compared with the output of concatenated SVGs working with current browsers. (See the SVG from OpenSea with composable SVGs)
The optional methods should be in optional interfaces.
Still need to agree if zindex and size are required in the EIP.
“For SVG-specific properties, the length unit identifier is optional. If a unit is not provided, the length value represents a distance in the current user coordinate system.” https://www.w3.org/TR/SVG11/types.html#DataTypeLength
Ah! That makes total sense. Otherwise the composer would have to parse the SVG to get the total size. Probably a good example to put in the EIP’s Rationale section!
Oh, I didn’t mean to imply that there wouldn’t be a z-index in the composer. Just that you’d probably have setBackground(address, uint256) and addAttachment(address, uint256) in the composer, and those would know what z-index to use, instead of the child only being usable at a single z-index.
I think this step could be implemented by preprocessing the SVG, and while it would increase the amount of work done off-chain, I think it might be worth while to reduce the amount of work on-chain. For example, this would be super useful to any NFT that currently base64 encodes data on the fly.
That said, I’d be happy with concatenation as well. Just wanted to throw the suggestion out there.
Concatenated SVG on-chain will work when the image size is small, and when the pfp image size is big (the image may have more details), the cost of uploading metadata will be very high (cyberbroker used nearly 100 ETH to upload their metadata). Also, the read gas could be very high and may exceed the gas limit.
SVG format has great composability, and it can combine multiple PNG images into an SVG image by smart contracts in a very efficient way. It will release all artists’ creativity by enabling PNG image composability.
But the PNG files will consume a lot of storage, so we can either upload them to Ethereum by consuming a lot of gas or use a programmable storage L2 to store the PNG and assemble them according to the state of L1’s NFT.
We have a super cool demo here and use Web3Q as the programmable storage L2 layer.
Really cool EIP. Thanks for writing it and building such cool projects!
Just reading it and got a few comments
A) The EIP should focus on the technical specification only
If the caller of the render function composes an SVG NFT they must have rights to use the SVG body in a composable SVG. The token holder could optionally hold both the SVG and rendered SVG NFTs or the token holder could hold the SVG NFT, and the SVG NFT could hold the rendered SVG NFT.
This should not be specified in my opinion and I strongly feel about this, the EIP should focus on the technical spec.
B) viewbox information
I see that the rendered svg omits the SVG tag so it can be composed more easily, unfortunately it removes an important aspect of many SVG: the viewbox on which they were designed for.
As such we should somehow add that back in the returned data.
I’d be ok with removing text around ensuring that users have appropriate rights before composing from the EIP. Legal rights to use will be on a project basis.
Providing potential options for linking NFTs could be useful, but also don’t have to be in the EIP.
An early draft of the EIP included dimensions and a z-index. We stripped this back to just the renderByTokenId function.
I had a technical question about the SVG spec version considered. Should the renderTokenById function only support SVG 1.1 or SVG 2 (currently still in Candidate Recommendation) is OK ?
Concerning the viewBox, I made a few tests with a composable SVG marker. And after grabbing some random SVGs on the internet, it appears that wrapping the content in an <svg viewBox="0 0 X X"> (if not already present) was mandatory to control the final rendering.
This can be done either by the composed SVG NFT or by the composable SVG NFT.
Which raise 2 questions for me:
What do I want to return in the renderTokenById and control the rendering (to allow full composition with svg elements and taking the risk of being cropped or restrict it) ? And also should it be the same as tokenURI ?
Who should handle it ?
Both are outside of this EIP for me and I like the simple yet very efficient renderTokenById(uint256 id).
I think a recommandation guide would help heading in the same direction.
Also maybe later, an additional function returning an ENUM or an identifier, could specify the rendering, allowed composition uses and maybe pricing (a new kind of royalties/fees for using this composable NFT) ?
We have been returning an SVG group element, with accessories, background and pfp all using the same coordinates (viewBox) for Party Panda 2.0/Merge Bears. Essentially not including the <svg> element.
So tokenURI includes the svg element whilst renderTokenById is just grouped elements.
It was suggested by one of the editors that we standardize viewBox="0 0 500 500". An early draft of the EIP had sizing but we removed to keep the EIP as simple as possible.
I think the composer should handle any scaling/transforms, as it is their responsibility to compose the SVGs as they require.
I played with translate and scale on a Merge Bear. I used the renderTokenById function from a Loogie Bow and an OE40 and manually added to a Merge Bear in https://www.svgviewer.dev/ and adjusted the translate and scale so that it fit.
Example manual SVG with Merge Bear, Crown, Bow and OE40:
Looking at this example, ERC4883 composers could optionally accept translate and scale so that the end user could craft the SVG as desired.
Agree. We are still very early, as we work out how to compose, especially across different ERC4883 projects.
I removed the rights requirement from the EIP as suggested by @wighawag
I would like to see a license that allows an ERC4883 to be composed by it’s holder as long as not used in hate speech. (something along the lines of The Can’t Be Evil NFT Licenses - a16z crypto
It’s not fully backwards compatible but is “builds upon SVG 1.1 Second Edition”.
So it won’t be a problem for most SVG elements but there still is a few breaking changes SVG 2 breaking changes · w3c/svgwg Wiki · GitHub
My concern was raised by the <use> element <use> - SVG: Scalable Vector Graphics | MDN and its older xlink:href with the associated namespace xlink: that might not be supported at some point by browsers. But is still heavily used…
I am not sure it would be worth adding the SVG version to the EIP. as there is some backwards compatibility. If a composer attempts to compose with an incompatible SVG then the user can undo any composing/linking.
I just learned of this draft EIP, and wanted to add in another example “in the wild” that I helped develop:
The MoonCatRescue project allows anyone to design a new Accessory for MoonCats, and any MoonCat token-holders can purchase Accessories from the Boutique. There is an on-chain rendering contract at 0x91CF36c92fEb5c11D3F5fe3e8b9e212f7472Ec14 that outputs layered SVGs of the MoonCat, wearing the Accessories their owner chose to be active at that time. The MoonCats themselves are SVG-style illustrations (using <polygon> shapes), while the Accessories are PNG-style data, which are embedded into the SVG as data URLs (like how @qzhodl gave as an example).
Accessories themselves are defined in the contract at 0x8d33303023723dE93b213da4EB53bE890e747C63, which has the PNG-style image data (an IDAT chunk) stored separately from the dimensions and position data (there are four different MoonCat poses, so each Accessory has up to four different offset positions, to indicate how it should be positioned on each of the four poses). I went into more details about how that contract works in this Reddit post.
I think having a common way to name the composition/data information for assets like these would be a great addition to the space! Though I think each asset that’s intended to be an Accessory built onto some sort of base thing would need multiple position definitions (for how to position it on multiple base types of things), or would need to have the asset have its origin put at a specific spot on its artwork, and declare what “type” it is (e.g. all “neckwear” assets have their origin be where the center of the neck should be, and its “width” define how wide the neck is. That would be enough information for any sort of character to position the asset correctly, and scale it to fit their “neck”).