EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8021: Transaction Attribution

Leverage data suffixes to enables interoperable transaction attribution for apps, wallets, and more.

Authors
Created2025-09-11
Discussion Linkhttps://ethereum-magicians.org/t/erc-8021-transaction-attribution/25561
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#1209PR open

EIP-GPT summary

Contents
AbstractMotivationSpecificationData Suffix SchemaMulti-Attribution SchemasExamplesParsing AlgorithmCode Registry InterfaceCode Registry Canonical AddressesCode MetadataERC-4337 User Operation ImplementationApp<>Wallet IntegrationethsendTransaction IntegrationwalletsendCalls Integration (ERC-5792)RationaleDesign DecisionsAlternative Approaches ConsideredForwards CompatibilityBackwards CompatibilityTest CasesSecurity ConsiderationsParsing SafetyRegistry TrustData IntegrityPrivacy ConsiderationsCopyright

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:

  1. Non-interoperable attribution: Each application uses custom suffix formats, requiring explicit checks for specific patterns to detect attributions
  2. Opaque identification: Hash-based suffixes obscure the actual application identity, making attribution analysis difficult
  3. Reward distribution challenges: Even when an application is identified, there's no standardized way to determine where to send rewards
  4. Limited multi-entity support: No clear mechanism for attributing transactions to multiple entities (e.g., both application and wallet)
  5. 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):

  1. ercMarker (16 bytes): A special identifier value indicating use of this standard ("8021" repeated)
  2. schemaId (1 byte): Schema ID number that declares the schema of proceeding bytes with space for forwards compatibility
  3. schemaData (variable bytes): Data to be parsed according to schemaId

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:

  1. codesLength (1 byte): Length of the codes array in bytes, including delimiters
  2. codes (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

  1. codesLength (1 byte): Length of the codes array in bytes, including delimiters
  2. codes (variable length): Array of attribution codes using ASCII encoding with comma (0x2C) as delimiter
  3. codeRegistryChainIdLength (1 byte): Length of the chain id hosting the custom Code Registry
  4. codeRegistryChainId (variable bytes): Chain id hosting the custom Code Registry
  5. codeRegistryAddress (20 bytes): Address of a smart contract implementing ICodeRegistry

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
  1. cborLength (2 bytes): Length of the CBOR-encoded map in bytes
  2. 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.

KeyDescription
aApplication code - string value
wWallet code - string value
sService codes - array of string values identifying additional service providers (e.g., block builders, relayers, solvers)
rCustom 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)
mCustom 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||ercMarker

Single entity attribution with canonical registry:

txData||"baseapp"||7||0||0x80218021802180218021802180218021

Multiple entity attribution with custom registry:

txData||0xCodeRegistry||8453||2||"baseapp,morpho"||14||1||0x80218021802180218021802180218021

Single 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:

  1. Extract the last 16 bytes as `ercMarker`` and verify it matches the standard identifier
  2. Extract the previous byte as schemaId
  3. If schemaId is 0``: a. Extract the previous byte as codesLength b. Extract the previous codesLength bytes as codes c. Implicitly set codeRegistryAddress as the registry defined by the host chain d. Parse codes using 0x2C (comma) as delimiter between codes e. Query code-related data from codeRegistryAddress on host chain
  4. If schemaId is 1: a. Extract the previous byte as codesLength b. Extract the previous codesLength bytes as codes c. Extract the previous byte as codeRegistryChainIdLength d. Extract the previous codeRegistryChainIdLength bytes as codeRegistryChainId e. Extract the previous 20 bytes as codeRegistryAddress f. Parse codes using 0x2C (comma) as delimiter between codes g. Query code-related data from codeRegistryAddress on chain with id codeRegistryChainId
  5. If schemaId is 2: a. Extract the previous 2 bytes as cborLength b. Extract the previous cborLength bytes as cborData c. Decode cborData as a CBOR map d. Extract application code from key a e. Extract wallet code from key w (if present) f. Extract service codes from key s (if present) as an array of strings g. Extract custom registries from key r (if present), containing sub-maps keyed by a (application) and/or w (wallet), each with c (chainId) and a (address) h. Extract metadata from key m (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
  6. Else schemaId does 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:

  1. Applications SHOULD append the suffix to userOp.callData when constructing user operations
  2. Parsers MUST examine the callData field of user operations rather than transaction data when processing ERC-4337 transactions
  3. 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:

  1. eth_sendTransaction
  2. wallet_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: canonical

Multiple entity attribution + custom registry:

Input: 0xddddddddcccccccccccccccccccccccccccccccccccccccc210502626173656170702C6D6F7270686F0E0180218021802180218021802180218021
Expected:
- txData: 0xdddddddd
- schemaId: 1
- codes: ["baseapp", "morpho"]
- codeRegistryChainId: 8453
- codeRegistryAddress: 0xcccccccccccccccccccccccccccccccccccccccc

Single 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 schemaId

Security 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 and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth