An interface for better identification and detection of SRCs by number.
It designates a field called majorSRCIdentifier which is normally known or referred to as “SRC number”. For example, SRC-721 has a majorSRCIdentifier = 721. This SRC has a majorSRCIdentifier = 5269.
Calling it a majorSRCIdentifier instead of ERCNumber makes it future-proof: anticipating there is a possibility where future SRCs are not numbered or if we want to incorporate other types of standards.
It also proposes a new concept of minorSRCIdentifier which is left for authors of
individual SRC to define. For example, SRC-721’s author may define SRC721Metadata
interface as minorSRCIdentifier= keccak256("SRC721Metadata").
It also proposes an event to allow smart contracts to optionally declare the SRCs they support.
Motivation
This SRC is created as a competing standard for SRC-165.
Here are the major differences between this SRC and SRC-165.
SRC-165 uses the hash of a method’s signature which declares the existence of one method or multiple methods,
therefore it requires at least one method to exist in the first place. In some cases, some SRC interfaces do not have a method, such as some SRCs related to data format and signature schemes or the “Soul-Bound-ness” aka SBT which could just revert a transfer call without needing any specific method.
SRC-165 doesn’t provide query ability based on the caller.
The compliant contract of this SRC will respond to whether it supports certain SRC based on a given caller.
Here is the motivation for this SRC given SRC-165 already exists:
Using SRC numbers improves human readability as well as make it easier to work with named contracts such as ENS.
Instead of using an SRC-165 identifier, we have seen an increasing interest to use SRC numbers as the way to identify or specify an SRC. For example
SRC-5267 specifies extensions to be a list of SRC numbers.
SRC-600, and SRC-601 specify an SRC number in the m / purpose' / subpurpose' / SRC' / wallet' path.
SRC-5568 specifies The instruction_id of an instruction defined by an SRC MUST be its SRC number unless there are exceptional circumstances (be reasonable)
SRC-6120 specifies struct Token { uint sip; ..., } where uint sip is an SRC number to identify SRCs.
SRC-867 (Stagnant) proposes creating an erpId, described as a string identifier for that ERP, likely based on the associated SRC number.
Having an SRC/SRC number detection interface reduces the need for a lookup table in smart contract to
convert a function method or whole interface in any SRC in the bytes4 SRC-165 identifier into its respective SRC number and massively simplifies the way to specify SRC for behavior expansion.
We also recognize a smart contract might have different behavior given different caller accounts. One of the most notable use cases is that when using Transparent Upgradable Pattern, a proxy contract gives an Admin account and Non-Admin account different treatment when they call.
Specification
In the following description, we use SRC and SRC interchangeably. This was because while most of the time the description applies to an SRC category of the Standards Track of SRC, the SRC number space is a subspace of SRC number space and we might sometimes encounter SRCs that aren’t recognized as SRCs but has behavior that’s worthy of a query.
Any compliant smart contract MUST implement the following interface
// DRAFTv1
pragmasolidity^0.8.9;interfaceISRC5269{eventOnSupportERC(addressindexedcaller,// when emitted with `address(0x0)` means all callers.
uint256indexedmajorSRCIdentifier,bytes32indexedminorSRCIdentifier,// 0 means the entire SRC
bytes32ercStatus,bytesextraData);/// @dev The core method of SRC Interface Detection
/// @param caller, a `address` value of the address of a caller being queried whether the given SRC is supported.
/// @param majorSRCIdentifier, a `uint256` value and SHOULD BE the SRC number being queried. Unless superseded by future SRC, such SRC number SHOULD BE less or equal to (0, 2^32-1]. For a function call to `supportERC`, any value outside of this range is deemed unspecified and open to implementation's choice or for future SRCs to specify.
/// @param minorSRCIdentifier, a `bytes32` value reserved for authors of individual SRC to specify. For example the author of [SRC-721](/SRCS/sip-721) MAY specify `keccak256("SRC721Metadata")` or `keccak256("SRC721Metadata.tokenURI")` as `minorSRCIdentifier` to be queried for support. Author could also use this minorSRCIdentifier to specify different versions, such as SRC-712 has its V1-V4 with different behavior.
/// @param extraData, a `bytes` for [SRC-5750](/SRCS/sip-5750) for future extensions.
/// @return ercStatus, a `bytes32` indicating the status of SRC the contract supports.
/// - For FINAL SRCs, it MUST return `keccak256("FINAL")`.
/// - For non-FINAL SRCs, it SHOULD return `keccak256("DRAFT")`.
/// During SRC procedure, SRC authors are allowed to specify their own
/// ercStatus other than `FINAL` or `DRAFT` at their discretion such as `keccak256("DRAFTv1")`
/// or `keccak256("DRAFT-option1")`and such value of ercStatus MUST be documented in the SRC body
functionsupportERC(addresscaller,uint256majorSRCIdentifier,bytes32minorSRCIdentifier,bytescalldataextraData)externalviewreturns(bytes32ercStatus);}
In the following description, SRC_5269_STATUS is set to be keccak256("DRAFTv1").
In addition to the behavior specified in the comments of ISRC5269:
Any minorSRCIdentifier=0 is reserved to be referring to the main behavior of the SRC being queried.
The Author of compliant SRC is RECOMMENDED to declare a list of minorSRCIdentifier for their optional interfaces, behaviors and value range for future extension.
When this SRC is FINAL, any compliant contract MUST return an SRC_5269_STATUS for the call of supportERC((any caller), 5269, 0, [])
Note: at the current snapshot, the supportERC((any caller), 5269, 0, []) MUST return SRC_5269_STATUS.
Any complying contract SHOULD emit an OnSupportERC(address(0), 5269, 0, SRC_5269_STATUS, []) event upon construction or upgrade.
Any complying contract MAY declare for easy discovery any SRC main behavior or sub-behaviors by emitting an event of OnSupportERC with relevant values and when the compliant contract changes whether the support an SRC or certain behavior for a certain caller or all callers.
For any SRC-XXX that is NOT in Final status, when querying the supportERC((any caller), xxx, (any minor identifier), []), it MUST NOT return keccak256("FINAL"). It is RECOMMENDED to return 0 in this case but other values of ercStatus is allowed. Caller MUST treat any returned value other than keccak256("FINAL") as non-final, and MUST treat 0 as strictly “not supported”.
The function supportERC MUST be mutability view, i.e. it MUST NOT mutate any global state of SVM.
Rationale
When data type uint256 majorSRCIdentifier, there are other alternative options such as:
(1) using a hashed version of the SRC number,
(2) use a raw number, or
(3) use an SRC-165 identifier.
The pros for (1) are that it automatically supports any evolvement of future SRC numbering/naming conventions.
But the cons are it’s not backward readable: seeing a hash(SRC-number) one usually can’t easily guess what their SRC number is.
We choose the (2) in the rationale laid out in motivation.
We have a bytes32 minorSRCIdentifier in our design decision. Alternatively, it could be (1) a number, forcing all SRC authors to define its numbering for sub-behaviors so we go with a bytes32 and ask the SRC authors to use a hash for a string name for their sub-behaviors which they are already doing by coming up with interface name or method name in their specification.
Alternatively, it’s possible we add extra data as a return value or an array of all SRC being supported but we are unsure how much value this complexity brings and whether the extra overhead is justified.
Compared to SRC-165, we also add an additional input of address caller, given the increasing popularity of proxy patterns such as those enabled by SRC-1967. One may ask: why not simply use msg.sender? This is because we want to allow query them without transaction or a proxy contract to query whether interface SRC-number will be available to that particular sender.
We reserve the input majorSRCIdentifier greater than or equals 2^32 in case we need to support other collections of standards which is not an SRC/SRC.
Test Cases
describe("SRC5269",function(){asyncfunctiondeployFixture(){// ...}describe("Deployment",function(){// ...it("Should emit proper OnSupportERC events",asyncfunction(){let{txDeployErc721}=awaitloadFixture(deployFixture);letevents=txDeployErc721.events?.filter(event=>event.event==='OnSupportERC');expect(events).to.have.lengthOf(4);letev5269=events!.filter((event)=>event.args!.majorSRCIdentifier.eq(5269));expect(ev5269).to.have.lengthOf(1);expect(ev5269[0].args!.caller).to.equal(BigNumber.from(0));expect(ev5269[0].args!.minorSRCIdentifier).to.equal(BigNumber.from(0));expect(ev5269[0].args!.ercStatus).to.equal(ethers.utils.id("DRAFTv1"));letev721=events!.filter((event)=>event.args!.majorSRCIdentifier.eq(721));expect(ev721).to.have.lengthOf(3);expect(ev721[0].args!.caller).to.equal(BigNumber.from(0));expect(ev721[0].args!.minorSRCIdentifier).to.equal(BigNumber.from(0));expect(ev721[0].args!.ercStatus).to.equal(ethers.utils.id("FINAL"));expect(ev721[1].args!.caller).to.equal(BigNumber.from(0));expect(ev721[1].args!.minorSRCIdentifier).to.equal(ethers.utils.id("SRC721Metadata"));expect(ev721[1].args!.ercStatus).to.equal(ethers.utils.id("FINAL"));// ...});it("Should return proper ercStatus value when called supportERC() for declared supported SRC/features",asyncfunction(){let{src721ForTesting,owner}=awaitloadFixture(deployFixture);expect(awaitsrc721ForTesting.supportERC(owner.address,5269,ethers.utils.hexZeroPad("0x00",32),[])).to.equal(ethers.utils.id("DRAFTv1"));expect(awaitsrc721ForTesting.supportERC(owner.address,721,ethers.utils.hexZeroPad("0x00",32),[])).to.equal(ethers.utils.id("FINAL"));expect(awaitsrc721ForTesting.supportERC(owner.address,721,ethers.utils.id("SRC721Metadata"),[])).to.equal(ethers.utils.id("FINAL"));// ...expect(awaitsrc721ForTesting.supportERC(owner.address,721,ethers.utils.id("WRONG FEATURE"),[])).to.equal(BigNumber.from(0));expect(awaitsrc721ForTesting.supportERC(owner.address,9999,ethers.utils.hexZeroPad("0x00",32),[])).to.equal(BigNumber.from(0));});it("Should return zero as ercStatus value when called supportERC() for non declared SRC/features",asyncfunction(){let{src721ForTesting,owner}=awaitloadFixture(deployFixture);expect(awaitsrc721ForTesting.supportERC(owner.address,721,ethers.utils.id("WRONG FEATURE"),[])).to.equal(BigNumber.from(0));expect(awaitsrc721ForTesting.supportERC(owner.address,9999,ethers.utils.hexZeroPad("0x00",32),[])).to.equal(BigNumber.from(0));});});});
Similar to SRC-165 callers of the interface MUST assume the smart contract
declaring they support such SRC interfaces doesn’t necessarily correctly support them.
Zainan Victor Zhou (@xinbenlv), "SRC-5269: SRC Detection and Discovery [REVIEW]," Sila Improvement Proposals, no. 5269, July 2022. Available: https://sips.sila.org/SIPS/sip-5269.