Abstract
This ERC defines a standardized method for attributing transactions to applications, wallets, and other entities through structured data suffixes appended to transaction calldata. The standard enables interoperable attribution tracking, supports multiple entity attribution per transaction, and provides a mechanism for reward distribution through registered payout addresses.
Motivation
Currently, applications informally append "data suffixes" to transactions to indicate their origin, typically using the first bytes of a hash representing the application name or domain (e.g. keccak256("AppName")[0:4]).
This ad-hoc approach presents several limitations:
- Non-interoperable attribution: Each application uses custom suffix formats, requiring explicit checks for specific patterns to detect attributions
- Opaque identification: Hash-based suffixes obscure the actual application identity, making attribution analysis difficult
- Reward distribution challenges: Even when an application is identified, there's no standardized way to determine where to send rewards
- Limited multi-entity support: No clear mechanism for attributing transactions to multiple entities (e.g., both application and wallet)
- Lack of extensibility: No standardized way to query additional metadata about attributed entities
This standard addresses these issues by providing a structured, extensible format for transaction attribution that supports multiple entities, reward distribution, and metadata discovery.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Data Suffix Schema
This standard defines a structured data suffix format appended to transaction calldata. The suffix consists of the following components (ordered from end of calldata backwards):
ercMarker(16 bytes): A special identifier value indicating use of this standard ("8021" repeated)schemaId(1 byte): Schema ID number that declares the schema of proceeding bytes with space for forwards compatibilityschemaData(variable bytes): Data to be parsed according toschemaId
A schemaId MUST be defined in an ERC to be considered valid.
All multi-byte integer fields in this specification use big-endian byte order.
Multi-Attribution Schemas
Our first schemas enable attributing multiple entities in a single transaction.
Entities are identified by a unique "code" which is a string that follows 7-bit ASCII encoding. A code MUST NOT contain commas (0x2C) as this is reserved as a delimiter for attributing multiple codes.
Codes MUST also be registered in a Code Registry smart contract (interface defined in later section). Chains are encouraged to provide canonical code registries which MAY be defined in this or future ERCs.
Schema ID 0 defines the following bytes and assumes chain's canonical Code Registry:
codesLength(1 byte): Length of the codes array in bytes, including delimiterscodes(variable length): Array of attribution codes using ASCII encoding with comma (0x2C) as delimiter
Schema ID 1 builds on top of 0, but also defines a custom Code Registry chain and address
codesLength(1 byte): Length of the codes array in bytes, including delimiterscodes(variable length): Array of attribution codes using ASCII encoding with comma (0x2C) as delimitercodeRegistryChainIdLength(1 byte): Length of the chain id hosting the custom Code RegistrycodeRegistryChainId(variable bytes): Chain id hosting the custom Code RegistrycodeRegistryAddress(20 bytes): Address of a smart contract implementingICodeRegistry
Custom Code Registries can be referenced across chains for ease of connecting identity for multi-chain apps.
Schema ID 2 uses CBOR encoding for extensible transaction annotation:
- Optional and unknown fields for future extensions
- Coexistence with other suffix-based systems
- Arbitrary metadata without requiring new schema IDs
- cborLength (2 bytes): Length of the CBOR-encoded map in bytes
- cborData (variable length): CBOR-encoded map containing attribution data
The CBOR map MUST use the following key schema. All fields are OPTIONAL. All top-level keys are reserved for this specification.
| Key | Description |
|---|---|
a | Application code - string value |
w | Wallet code - string value |
s | Service codes - array of string values identifying additional service providers (e.g., block builders, relayers, solvers) |
r | Custom registries - sub-map keyed by entity type (a for application, w for wallet), each containing c (chainId as hex string) and a (20-byte hex-encoded address string) |
m | Custom metadata - sub-map of arbitrary key-value pairs |
Only the m (metadata) field MAY contain arbitrary data defined by consumers of this specification. All other top-level keys are reserved and MUST NOT be used for purposes other than those defined above.
type Schema2Data = {
a?: string;
w?: string;
s?: string[];
r?: {
a?: { c: Hex; a: Hex };
w?: { c: Hex; a: Hex };
};
m?: Record<string, unknown>;
};Note: Schema ID 2 trades minimal byte size for extensibility. Applications prioritizing minimal calldata overhead SHOULD use Schema ID 0 or 1 when their use case does not require extensibility.
Examples
The complete suffix format is:
txData||schemaData||schemaId||ercMarkerSingle entity attribution with canonical registry:
txData||"baseapp"||7||0||0x80218021802180218021802180218021Multiple entity attribution with custom registry:
txData||0xCodeRegistry||8453||2||"baseapp,morpho"||14||1||0x80218021802180218021802180218021Single entity attribution with CBOR (Schema ID 2):
{txData}{"a":"baseapp"}{0x000b}{2}{0x80218021802180218021802180218021}Multiple entities with wallet, custom registry, and metadata (Schema ID 2):
{txData}{cborData}{0x007b}{2}{0x80218021802180218021802180218021}
{
"a": "baseapp",
"w": "privy",
"r": {
"a": {
"c": "0x2105",
"a": "0xBcf2B9598Ec0781eE49230CaACF0FB3C881B5195"
}
},
"m": {
"utm_campaign": "winter-promo",
"source": "webapp"
}
}Multiple entities with services (Schema ID 2):
{txData}{cborData}{0x0026}{2}{0x80218021802180218021802180218021}
{
"a": "baseapp",
"w": "privy",
"s": ["flashbots", "titan"]
}Parsing Algorithm
Offchain parsers SHOULD implement the following algorithm:
- Extract the last 16 bytes as `ercMarker`` and verify it matches the standard identifier
- Extract the previous byte as
schemaId - If
schemaIdis0``:a. Extract the previous byte ascodesLengthb. Extract the previouscodesLengthbytes ascodesc. Implicitly setcodeRegistryAddressas the registry defined by the host chain d. Parsecodesusing0x2C(comma) as delimiter between codes e. Query code-related data fromcodeRegistryAddresson host chain - If
schemaIdis1: a. Extract the previous byte ascodesLengthb. Extract the previouscodesLengthbytes ascodesc. Extract the previous byte ascodeRegistryChainIdLengthd. Extract the previouscodeRegistryChainIdLengthbytes ascodeRegistryChainIde. Extract the previous 20 bytes ascodeRegistryAddressf. Parsecodesusing0x2C(comma) as delimiter between codes g. Query code-related data fromcodeRegistryAddresson chain with idcodeRegistryChainId - If
schemaIdis2: a. Extract the previous 2 bytes ascborLengthb. Extract the previouscborLengthbytes ascborDatac. DecodecborDataas a CBOR map d. Extract application code from keyae. Extract wallet code from keyw(if present) f. Extract service codes from keys(if present) as an array of strings g. Extract custom registries from keyr(if present), containing sub-maps keyed bya(application) and/orw(wallet), each withc(chainId) anda(address) h. Extract metadata from keym(if present) i. For each code, if no custom registry specified for that entity type, use chain's canonical Code Registry j. Query code-related data from appropriate registry for each code - Else
schemaIddoes not match, parsing SHOULD terminate (unknown schemaId)
Code Registry Interface
A Code Registry MUST implement the following interface:
interface ICodeRegistry {
/// @notice Fetch a code's address to payout incentives
/// @param code The code to query
/// @return The address to receive rewards for this code
function payoutAddress(string memory code) external view returns (address);
/// @notice Fetch a code's URI to fetch for additional metadata
/// @param code The code to query
/// @return The URI pointing to metadata about this code
function codeURI(string memory code) external view returns (string memory);
/// @notice Check if a code is valid format (characters, length, etc)
/// @dev MUST return false if code is empty string
/// @param code The code to query
/// @return True if the code is valid, false otherwise
function isValidCode(string memory code) external view returns (bool);
/// @notice Check if a code is registered
/// @param code The code to query
/// @return True if the code is registered, false otherwise
function isRegistered(string memory code) external view returns (bool);
}Code Registry Canonical Addresses
Schema 0 supports smallest data suffixes by allowing attribution to implicitly use a registry canonically defined by the chain. Chains that offer canonical registries should make their addresses publicly available in their documentation.
Code Metadata
Querying CodeRegistry.codeURI(code) returns a string uri to fetch actual metadata, following from NFT patterns. The returned metadata SHOULD include these fields:
type CodeMetadata = {
app: {
name: string;
url: string;
};
};Future ERCs can extend this schema.
ERC-4337 User Operation Implementation
For ERC-4337 compliant smart accounts, the implementation differs from normal Ethereum transactions:
Normal Transactions: The attribution suffix is appended to the data field of the top-level Ethereum transaction.
ERC-4337 User Operations: The attribution suffix MUST be appended to the userOp.callData field instead of the transaction-level data field, since smart account transactions are executed through the EntryPoint contract where the actual call data is contained within the user operation structure.
When implementing attribution for smart accounts:
- Applications SHOULD append the suffix to
userOp.callDatawhen constructing user operations - Parsers MUST examine the
callDatafield of user operations rather than transaction data when processing ERC-4337 transactions - Wallets supporting both EOA and smart account transactions MUST handle suffix appending appropriately based on the account type
This ensures attribution data remains accessible and parseable regardless of whether the transaction originates from an externally owned account (EOA) or a smart contract account following ERC-4337.
App<>Wallet Integration
Apps can append attribution through multiple methods, but this standard defines integration with two RPCs:
eth_sendTransactionwallet_sendCalls
Regardless of integration method, wallets MAY insert their own attribution code alongside the application-defined codes, provided they understand this standard.
eth_sendTransaction Integration
This legacy method is supported by all functional Ethereum wallets. Apps can add attribution to their transactions by directly appending the data suffix to the data field.
Note that wallets MAY mutate the transaction, for example in cases where ERC-4337 Smart Accounts are used and a transaction is actually sent from an ERC-4337 Bundler.
wallet_sendCalls Integration (ERC-5792)
This more modern alternative to eth_sendTransaction also has wide adoption across wallets and provided simpler interfaces for batching transactions, sponsoring gas, and more open-ended capabilities.
Applications using ERC-5792's wallet_sendCalls RPC method MUST communicate attribution data through a new dataSuffix capability:
interface DataSuffixCapability {
dataSuffix: {
value: string; // Hex-encoded bytes to append as suffix
optional?: bool; // Declare to wallet if adding attribution is optional (reverts if not and wallet does not support)
};
}Wallets implementing this capability MUST append the provided dataSuffix bytes to the proper data location (transaction data for EOAs, user operation callData for ERC-4337 Smart Accounts).
Rationale
Design Decisions
Reverse-order parsing: The suffix format is designed for parsing from the end of calldata backwards, enabling efficient detection of attribution without requiring knowledge of the original calldata length.
schemaId byte: Including a schemaId field enables future extensions and optimizations in minimizing attribution data.
Registry pattern: Using a registry contract allows entities to update their payout addresses and metadata without requiring transaction format changes.
ASCII encoding: Using human-readable ASCII codes improves debuggability and allows for meaningful code names rather than opaque hashes.
ERC-5792 integration: The capability-based approach aligns with ERC-5792's extensible design while providing a clean separation of concerns between applications and wallets.
CBOR encoding for Schema ID 2: Schema IDs 0 and 1 are optimized for minimal bytes but assume sole ownership of the calldata suffix. Schema ID 2 uses CBOR encoding to enable coexistence with other suffix-based systems, support arbitrary metadata, and allow future extensions without requiring new schema IDs. The tradeoff is increased calldata size and parsing complexity.
Alternative Approaches Considered
Customizable registry: While a single global registry is recommended when available for reduced transaction data, the flexible registry approach allows different ecosystems to maintain their own attribution systems.
Fixed-length codes: A fixed-length encoding was considered but rejected to allow for flexible, human-readable identifiers.
ERC-721 dependency: While codes may be implemented as ERC-721 NFTs, reducing strict dependencies makes this proposal more flexible and clarifies the minimal interface truly required for attribution.
Structured ERC-5792 capability: A generic bytes dataSuffix minimizes the effort on wallets to integrate and maintain support for new schemaIds, increasing chances of wide adoption.
Forwards Compatibility
All schemas after schema V2 must follow the following structure: {schemaData}{schemaDataLength (2 bytes)}{schemaId (1 byte)}{ercSuffix (16 bytes)}
This layout ensures the complete dataSuffix for any future schema can be deterministically discovered using the same parsing rules.
Backwards Compatibility
This standard is fully backward compatible with existing transaction formats. Transactions without attribution suffixes continue to function normally, and the presence of attribution data does not affect transaction execution.
Existing applications using informal attribution methods can migrate to this standard while maintaining their current functionality during the transition period.
Test Cases
Single entity attribution + canonical registry:
Input: 0xdddddddd62617365617070070080218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 0
- codes: ["baseapp"]
- registry: canonicalMultiple entity attribution + custom registry:
Input: 0xddddddddcccccccccccccccccccccccccccccccccccccccc210502626173656170702C6D6F7270686F0E0180218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 1
- codes: ["baseapp", "morpho"]
- codeRegistryChainId: 8453
- codeRegistryAddress: 0xccccccccccccccccccccccccccccccccccccccccSingle entity attribution with CBOR (Schema ID 2):
Input: 0xdddddddda161616762617365617070000b0280218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 2
- cborLength: 0x000b (11 bytes)
- cborData: {"a": "baseapp"}Multiple entities with metadata (Schema ID 2):
Input: 0xdddddddda46161676261736561707061776570726976796172a16161a26163663078323130356161782a307842636632423935393845633037383165453439323330436141434630464233433838314235313935616da26c75746d5f63616d706169676e6c77696e7465722d70726f6d6f66736f7572636566776562617070007b0280218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 2
- cborLength: 0x007b (123 bytes)
- cborData: {"a":"baseapp","w":"privy","r":{"a":{"c":"0x2105","a":"0xBcf2B9598Ec0781eE49230CaACF0FB3C881B5195"}},"m":{"utm_campaign":"winter-promo","source":"webapp"}}Attribution with services array (Schema ID 2):
Input: 0xdddddddda361616762617365617070617765707269767961738269666c617368626f747365746974616e00260280218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 2
- cborLength: 0x0026 (38 bytes)
- cborData: {"a":"baseapp","w":"privy","s":["flashbots","titan"]}Invalid schemaId handling:
Input: 0xddddddddff80218021802180218021802180218021
Expected: Parsing stops, unknown schemaIdSecurity Considerations
Parsing Safety
As mentioned earlier, we recommend parsers validate the suffix format before processing to prevent malformed data from causing errors. Invalid suffixes can be ignored rather than causing parsing failures.
Registry Trust
The security of attribution data depends on the trustworthiness of the registry contract. Users and applications are encouraged to verify the registry implementation and governance before relying on its data.
Data Integrity
Since attribution data is appended to calldata, it could potentially be modified by intermediaries. Applications requiring cryptographic proof of attribution should implement additional signature-based verification mechanisms.
Privacy Considerations
Attribution data is publicly visible onchain. Future standards can find ways to standardize privacy-preserving attribution, but this it out of scope for the initial proposal and can be added in the future.
Copyright
Copyright and related rights waived via CC0.
