Discussion on ERC-7583 for Inscribing assets in smart contract

I responded in the GitHub pull request with a technical issue with the proposal as-written. Posting here to also voice concerns about the overall structure of this EIP:

In general, I feel this doesn’t need to be an EIP, since it doesn’t need to be a standard that multiple contracts implement. If people wish to give meaning to arbitrary data that is just announced/timestamped on an EVM blockchain, it doesn’t need to be implemented by multiple smart contracts (since that mentality is aiming to skew away from smart contracts). Instead, just one contract needs to exist with a method anyone can call, to trigger an event with arbitrary data. And actually many of those already exist from the early days of Ethereum when people were experimenting with simple “greeter” contracts. What benefit is to be gained by a project organizer deploying a smart contract for their project that allows people to trigger “Inscribe” events, but doesn’t use any other smart contract benefits?

That “only” is discounting one other thing I think is important: Event indexers already exist. Web3 SDK libraries give easy access to filter and query Events (as they’re exposed by the JSON-RPC node APIs), but calldata isn’t as easily filterable. An additional benefit you get by “paying twice” is the ability for many other nodes to present your data in a more convenient package, and so will show up in some form in explorers, wallets, and portfolio tools without them needing to specifically modify to support it.

If this is to become an accepted EIP, a technical concern with the formatting chosen: If the data value of the event is forced to be a data URI, then why is it defined as a bytes value rather than a string value? I would suggest dropping the requirement that it be a data URI in format, and allow any data to be present. That allows for implementations to use whatever data encoding they prefer (e.g. protobuf or BSON). Using BSON or Solidity data encoding would encode the data more compactly (and therefore reduce the cost to inscribe). The data URI format needing to start with data: means every inscription has five extra bytes it needs to pay to store.

2 Likes

The Ethereum Name Service (ENS) specification allows putting arbitrary key/value pairs on domains/names you own. There are several standard keys that define avatar images and demographic information about the owner, but you can put whatever you want in a domain you own. If you wish to put some structured data there and create a project that derives meaning from that you definitely can. But other users cannot modify the key/value pairs on your domain. So it can be a way for individual users to signal something, but cannot be used to have a back-and-forth “conversation” or log with other users adding data to it.

Support innovation, the ETH ecosystem is in great need of more innovation and this time will promote the use of ETH in more scenarios

Thank you very much for your valuable comments and suggestions. We deeply agree with your opinion that merely relying on an “inscribe” event without the need for standardization through EIP is sufficient.At the same time, limiting data to a data URI format was a decision made in consideration of the richness of protocol content, but it indeed was not a thoroughly considered decision.

Over the past period, we have been deeply reflecting and exploring, trying to find out what the best asset form for inscriptions in the EVM should be. Based on these reflections, we have updated our results in the GitHub repository and developed a marketplace platform to validate the feasibility and practicality of the ERC7583 protocol.

We highly value your opinions and suggestions, which are crucial for the improvement and perfection of the ERC7583 protocol. Therefore, we are very eager to hear more specific suggestions and feedback from you. Would you be willing to provide more advice on how to improve our protocol and its implementation? We look forward to further communication and cooperation with you, and once again, thank you for your time and contribution.

True, but no “out of the box” event indexers give you enough data to do anything useful because Ethscription state can depend on Ethscriptions created arbitrarily far in the past. So if you want to index Ethscription state you’re going to need a special-purpose indexer.

Do any such indexers built on the event model exist? I am not aware of any, certainly no open source ones.

On the other hand, the calldata model has an open source indexer that works great right here!

Why not use this (or a fork of it) versus hoping someone builds a better one using events and forcing users to pay the extra cost in the mean time?

My point was that there is a robust querying mechanism for Events already part of the standard RPC calls that can be made to any node. If you want to query Events of type “Foo” from a specific contract, for a specific block range (“arbitrarily far in the past”), one API call gets that. The RPC infrastructure allows getting the calldata from a transaction, but I don’t believe it has any means to search/query based on calldata. So if you’re creating “inscriptions” that are stored as calldata, yes you’ll need a custom indexer to scape the entire transaction history to find them, and then present some sort of custom querying interface to know about them. If you’re creating “inscriptions” that save their data as Events, you don’t need to create a custom indexer, because all nodes already know how to find and present that data to you on-demand; you don’t need to pre-fetch it and index it, just query it when you want it from a node.

Even if you did build a complex protocol on top of Event data, and needed an index to scrape all the data first, creating an indexer for Event data would be dramatically faster and easier to write, since it would be able to skip all the transactions that were irrelevant without fetching them. Indexing calldata would need to iterate over every transaction, fetching that transaction’s data from a node, and then determining if it’s needed for the protocol.

The latest updates to the GitHub draft lay out a radically different structure, with ERC7583 defining a type of contract that is a (true) ERC20 and (renamed/tweaked) ERC721 contract at once. That hybrid blend of non-fungible and fungible token is something that non-inscription communities have been exploring lately and ERC7631 has come from that. ERC7631 seems to be pursuing the same goals as this updated ERC7583, in that users have their fungible tokens saved as discrete “bundles” of arbitrary value. The key difference is ERC7631 has the non-fungible “bundles” be true ERC721s. A significant downside of this updated ERC7583 is that the non-fungible part looks similar to ERC721s, but isn’t actually, and could confuse or break interfaces that assume they are.

ERC7583 having part of the contract’s state be accessible to other smart contracts (ERC20 interface) and the other part inaccessible (by attempting to save it as “an inscription”) seems like it’s significantly less useful. So, my opinion is that if a project wants the functionality of having a set of non-fungible tokens that “store” fungible token values for a user, we should help standardize ERC7631 as an interface to do that, to have the best compatibility with all other smart contracts in the space.

Practically I don’t think nodes will just give you everything in one API call. Alchemy as an example has this limit:

You can make eth_getLogs requests on any block range with a cap of 10K logs in the response OR a 2K block range with no cap on logs in the response and 150MB limit on the response size

I don’t think the assumption should be that users have access to a better node than Alchemy.

At a higher level though: events are better for devs, calldata is better for users. My suggestion is that to resolve this tension we look at the actual live ethscriptions protocol that has a robust open source reference indexer and hundreds of thousands of users rather than returning to first principles and deciding events v. calldata in the abstract.

Or is the suggestion that events are so much better than calldata that a new inscriptions protocol will, from a standing start, no users, no reference indexer implementation overtake what is already popular?

When we launched ethscriptions and iterated on it for over a year we considered all these topics and made the choices we did for considered reasons and with significant success.

I believe the best route for this ERC is to build on what’s working, not discard it. Or, if you want to discard it, to first get buy in from users and devs versus leaning on the standardization process itself to get support.

I have read the first commit of the ERC7631 implementation code, which was committed three weeks ago. And it is very similar to ERC404(Pandora).

I don’t know if you are aware of INSC. We deployed it on De-24-2023, at, and it was the first contract to attempt to combine ERC20 and ERC721. INSC was on the top trending list for 4 days until it was hacked.

Then, we spent a considerable amount of time developing a compensation plan in an effort to ensure our users receive fair compensation. And we also launched a new contract named INSC+ on Jan-17-2024, which introduces more innovation. We made this announcement at that time as well.

Because we didn’t fully grasp the cutthroat competition in the crypto industry, we set a restriction that the FT function could only be unlocked after all INSC+ tokens were claimed. Then, Pandora was launched on February 2, 2024. Following a similar story, it gained popularity and became increasingly well-known.

I mentioned this because you wanted me to build on ERC7631, akin to expecting a poor prince to serve a nouveau riche. Our story hasn’t been fully revealed yet. The significant downside of ERC7631 is that it requires using two contracts for one asset. This approach was proven to be ineffective by the project Cirth.

An NFT that points to off-chain content on IPFS or Arweave is incomplete, and thus not a digital artifact.
It’s a sign of weakness to abandon innovation just to conform to the existing NFT market.

2 Likes

That’s not my point; it would be impractical to get everything in one API call for most all projects. My point was in order to find what you wanted, less calls are needed when working with Event data than Calldata, and therefore searching through past history could be done live (possibly through a few iterations of eth_getLogs) or through a fast indexing service.

Consider the example: a project creates a protocol that includes users being able to send messages of type A, B, and C, each message having tags that indicate sender, recipient, and some message data.

If built with Events: on a web interface showing a profile for Alice, to display “all messages from Alice”, a single eth_getLogs call could get it (filtering for any message type, but where sender is Alice’s address) in one call, assuming Alice has sent less than 10,000 messages. A web interface wanting to show all recent activity for the whole project could query the latest 2,000 blocks as a “most recent actions” list, and load more if the user paginates through it.

If built with Calldata: on a web interface showing a profile for Alice, to display “all messages from Alice”, all transactions in all blocks from the launch of the project need to be inspected. There’s not a practical call to get that “live”, unless some custom indexer for the project has done the work already.

Consider creating a custom indexer for that theoretical project, to support a web interface wanting to do more complicated things like “list all messages to or from Alice, and any first-level replies to them”.

If built with Events: all events in transactions from the project’s smart contract need to be inspected and indexed. eth_getLogs allows querying chunks of 2,000 blocks at a time for all events for the project. That response object is a much smaller payload than all transactions in those 2,000 blocks, so the indexer doesn’t have to iterate over as much data (all the data returned is relevant; no need to filter/cull it first).

If built with Calldata: all transactions that interact with the project’s smart contract need to be parsed. In order to get “all transactions for a contract”, there’s no RPC call for that. The indexing service would need to go by single blocks, using eth_getBlockByNumber to fetch the block and the transactions in it, iterate over all the transactions and determine if they’re relevant, and then process them.

Am I missing something here that makes it easier to fetch calldata?

I’ve not heard that argument before; on what do you base that claim?

This discussion is about creating a potential standard. That is by its nature abstract, since a standard has a goal of being used by many different projects for different purposes. Standards are foundation layers that if built well, robust applications can be built upon them. “We’re already using it, and end-users like it” is not a stellar reason for a thing to become a standard.

No, I was unaware of that project previously. I wish you luck with your project, and can appreciate the struggles of trying to show a project achieved a technological feat before others.

However, in this thread, the discussion is on creating a standard, which therefore has a goal to be the best foundation for future projects to work from. Standards are open-source and while there is some prestige to authoring a standard, part of the natural process of standards is that they can evolve and grow through collaboration. Standards can get outdated and replaced by newer ones, without that being a stigma to the original standard.

Reading both ERC7583 (this one) and ERC7631 (“Dual Nature Token Pair”), they both appear to me to be having the same goal (represent a fungible and non-fungible thing together), and I think ideally we shouldn’t have multiple (active) standards for the same goal. This proposal encourages a calldata-based approach, which to me seems to be a significant disadvantage.

How is requiring two contracts “ineffective”?

That’s a personal opinion, and you’re welcome to personally label projects as you see fit. However, the ERC721 standard that most NFTs follow doesn’t require pointing to IPFS, Arweave, or similar, so ERC7631 deriving from that makes no opinion on “on-chain” vs “off-chain”, which I think is good, to allow different projects to choose how they wish. You are welcome to innovate and I’m glad you’re pursuing it. My intention was not that you “serve a nouveau riche”, but that in making the transition from “a project implementation” to “a standard”, that the best aspects of all projects who have been innovating in this space are considered.

Then does our INSC+ also use recursive methods?

IMO, a lot of the concepts that are here trying to be standardized, were already introduced by @middlemarch and the Ethscriber community. But if I may reinforce what some had previously pointed out: We should always prioritize saving the maximum amount of gas since the main purpose of this standard is to introduce a cheaper way to program historical data in Ethereum. The excess usage of events and especially duplicating entire calldata into event emission is by far the expected approach towards introducing a new standard supposed to save user’s gas. Just by having the calldata as a parameter in the function input we already have it indexed in the blockchain therefore there is no need to emit anything and rather fetch in the correct place.

Another criticism and this goes for all inscription implementers, is how our agenda pushes to apply conditions depending on the data passed, which is exactly how smart contracts operate. But if we want to automatize inscriptions at the smart contract level we should be looking into reserving some bytes, like the function selector, to specify what to do with the data and to warn the indexer about the type of data he is handling.

Another problem that should be tickled by the smart contract developers and indexer implementers is the subsequential internal calls. Current approaches assume the first calldata (input data) carries the inscription content, but this doesn’t provide enough backward compatibility with the EVM. The desirable scenario is when even low-level calls to other contracts carry a valid inscription and one where even more than one inscription could be tackled in a single transaction; e.g.: a contract that builds dynamic SVG’s as inscriptions upon mint. Using event emissions completes this task with ease, but it would also result in double gas spending. Sending a low-level call to a “fallbackless” address with zero value with the msg.data as the content prevails as the ultimate source of cheap.

If both standards are assumed to be valid in the future, we should also work on facilitating the job for indexers and dApps looking forward to implementing inscriptions on the VM. I assume this was proposed already in the past but I believe there should be a selector specially made for inscription identification when invoked as calldata, a default and easy-to-remember prefix such as 0xFFFFFFFF. While the event emission would also need a specific name to describe its inscription property like “ins_data”.

And far more important, the order in which we validate the calldatacopy() that will be read as the standard invocations:

input data (1st inscription)
├── internal calldata (2nd)
├── event emission (3rd)
        ├── internal calldata (4th)
        ├── event emission (5th)
                ├── ...

I hope these thoughts tickle everyone’s brain to find a cohesive path toward delivering a standard that the world can follow and resulting in gas savings for the ecosystem.