OTP-001: Supported Interfaces
This proposal recommends a way to introspect smart contracts and find out what interfaces they support.
Motivation
Currently, it is impossible to guess what a user wants to do with a contract or to determine clearly what a transaction is about, because there is no explicit method for identifying the contract’s purpose. Humans generally have to remember or guess the purpose in most cases.
Guide
When a human signs a transaction, they need to clearly understand what they are doing: minting, token transfer, staking, DAO voting. While Ethereum wallets support signing arbitrary structures, it is still not clear what you are signing and what the implications of doing so are. Similarly, explorers cannot clearly display what is happening.
Working with a specific contract begins with performing introspection—figuring out what the contract declares about itself. Once an app knows what a contract is about, it can build a good UI, show transaction history, and verify what a human attempts to sign.
This proposal describes a way to report which interfaces a contract supports.
Interfaces are defined in a free-form specification. Unlike most other approaches, this proposal defines an interface not only as the technical interface of a contract (get methods, internal messages, etc.) but also as a description of its behavior. Attaching a hash of the representation of a technical interface of a contract could cause conflicts between different standards, which is why this proposal defines interfaces loosely. It also allows an interface to be more fluid; for example, a token that cannot be transferred could simply be a contract that has a method can_transfer
returning false
. This would indicate that this token does not support transfers at all, without needing to implement the transfer method.
Interface IDs are hashes of reverse domain names (similar to packages in Java). This approach avoids name clashes between different teams if they want to build something exclusively for themselves.
Specification
In order to support introspection, the contract MUST implement the supports_interface
GET method:
(int...) supported_interfaces()
This method returns a list of supported interface codes. The first value MUST be hash("org.ton.introspection.v0")
= 123515602279859691144772641439386770278
.
If the first value is incorrect, the app MUST stop attempting to introspect the contract.
Example:
_ supported_interfaces() method_id { return (123515602279859691144772641439386770278);}
The hash of an interface is defined as the SHA256 hash truncated to 128 bits.
Drawbacks
This proposal doesn’t guarantee that the contract will behave correctly according to an interface. Also, it doesn’t provide a guaranteed way to avoid name clashes between different interfaces. This is a non-goal for this proposal.
This proposal isn’t tied to a specific technical interface. This could lead to multiple interfaces that do the same thing but have different IDs. This is a non-goal for this proposal, since a centralized registry would be very useful for existing interfaces, and a custom one would mostly be used in-house.
Rationale and alternatives
- Why 128 bit? We are looking at a global namespace that we need to maintain without conflicts. We cannot use anything much smaller since the probability of conflicts would be much higher. We are looking at UUID-like entropy, which is exactly 128-bit and time-proven. More than 128 bits would be too wasteful.
- Why freeform? As mentioned earlier, it is easier just to define an ID to start work early and then eventually build a standard. Additionally, interfaces (like ERC20) are usually not just technical interfaces but also include a set of rules defining how to work with them.
- Why not find out what a contract supports by decompiling? Explicit is always better than implicit in open-world scenarios. We cannot rely on our “disassembling” capabilities to perform introspection; even small errors could be fatal.
- Why not a hash of representation? Right now, there are no compilers that support this approach. Also, this proposal is future-proof. If anyone wants to build something more automated, they could easily produce their own hashes using their own rules while keeping everything consistent for external observers.