EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8026: Data Callback Capability

A way for apps to request user data from wallets

Authors
Created2025-09-17
Discussion Linkhttps://ethereum-magicians.org/t/erc-8026-data-callback-capability/25496
Requires
Referenced by

Markdown

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

EIP-GPT summary

Contents
AbstractMotivationSpecificationdataCallback CapabilityApp → WalletWallet → Service (HTTP Interface as Webhook Events)Wallet BehaviorWallet → App: Final user information in walletsendCalls responsewalletgetCapabilities ResponseRationaleBackwards CompatibilitySecurity ConsiderationsPrivacy ConsiderationsCopyright

Abstract

This proposal defines an EIP-5792 capability that allows apps to request user data. Additionally, this proposal specifies how wallets communicate with an app-provided callback URL so apps can check users' information and optionally modify transaction requests before users approve them.

Motivation

Apps often need user data such as contact and shipping information as part of a transaction request. Today, there is no standard for:

  • How apps describe the data they need from wallets.
  • How apps can validate user information before allowing them to proceed with a transaction (for example, ensuring a merchant can ship to a user's address before allowing them to complete a payment).
  • How wallets provide app-requested information, both during pre-signing validation and after a transaction request has been signed and submitted by a user.
  • How apps can update transaction requests based on user-provided information (for example, updating a payment request to include a shipping fee).

This capability provides a way for apps and wallets to communicate about this information.

Specification

One new EIP-5792 wallet capability is defined.

dataCallback Capability

The dataCallback capability is implemented by both apps and wallets.

App → Wallet

Apps include the capability when invoking wallet_sendCalls.

Capability object shape:

type DataCallbackType =
  | "email"
  | "phoneNumber"
  | "physicalAddress"
  | "name"
  | "onchainAddress";

type DataCallbackInfoRequest = {
  type: DataCallbackType;
  optional?: boolean; // default false
};

type DataCallbackEventType = "initiated" | "preSign" | "postSign";

type DataCallbackEvent =
  | {
      type: "initiated";
      context?: Record<string, unknown>; // arbitrary app-defined object
    }
  | {
      type: "preSign";
      requests: DataCallbackInfoRequest[]; // MUST be non-empty, no duplicate types
      context?: Record<string, unknown>; // arbitrary app-defined object
    }
  | {
      type: "postSign";
      context?: Record<string, unknown>; // arbitrary app-defined object
    };

type DataCallbackCapability = {
  callbackURL: string; // MUST be HTTPS
  events: DataCallbackEvent[]; // MUST be non-empty
};

Constraints:

  • events MUST be present and non-empty.
  • Each event's type MUST be one of exactly "initiated", "preSign", or "postSign". Wallets MUST reject unknown event names.
  • callbackURL MUST be a valid HTTPS URL.
  • requests is only permitted on preSign events and MUST be a non-empty array without duplicate type entries.
  • Each optional MUST be a boolean if present (defaults to false).
  • If type is onchainAddress, it MUST NOT be marked optional.
  • If a preSign event with requests is desired, a preSign event MUST be present in events (there is no default). events MUST NOT be omitted.

Examples:

{
  "capabilities": {
    "dataCallback": {
      "callbackURL": "https://example.com/callback",
      "events": [
        { "type": "initiated", "context": { "orderId": "123" } },
        {
          "type": "preSign",
          "requests": [
            { "type": "email" },
            { "type": "physicalAddress", "optional": true }
          ],
          "context": { "shippingTier": "express" }
        },
        { "type": "postSign", "context": { "webhookTag": "after" } }
      ]
    }
  }
}

Wallet → Service (HTTP Interface as Webhook Events)

If dataCallback.events is present, the wallet MUST send HTTPS POST requests to callbackURL for each event listed, using the JSON bodies defined below. The wallet MAY first collect missing data via its UI or user profile when handling preSign. Users MAY intentionally leave optional fields blank; wallets MUST omit intentionally blank fields from requestedInfo.

Shared types:

type RequestedInfo = {
  email?: string;
  name?: { firstName: string; lastName: string };
  onchainAddress?: `0x${string}`;
  physicalAddress?: {
    address1: string;
    address2?: string;
    city: string;
    state?: string;
    postalCode: string;
    countryCode: string; // ISO 3166-1 alpha-2
    name?: { firstName: string; familyName: string };
  };
  phoneNumber?: { number: string; country: string }; // E.164 number and ISO country code
};

type DataCallbackRequestEcho = {
  capabilities: Record<string, unknown>; // app-provided capabilities (may be updated by service during preSign only)
  calls: {
    to: `0x${string}`;
    data?: `0x${string}`;
    value?: `0x${string}`;
  }[];
  chainId: `0x${string}`; // hex string (e.g., '0x1')
};

Event payloads (wallet → callbackURL):

// Initiated (read-only notification)
type DataCallbackInitiatedEvent = {
  event: "initiated";
  context?: Record<string, unknown>;
  request: DataCallbackRequestEcho; // original values
};

// Pre-sign (allows validation and mutation)
type DataCallbackPreSignEvent = {
  event: "preSign";
  context?: Record<string, unknown>;
  requestedInfo: RequestedInfo; // only keys requested and available are included
  request: DataCallbackRequestEcho; // current values
};

// Post-sign (read-only notification)
type DataCallbackPostSignEvent = {
  event: "postSign";
  context?: Record<string, unknown>;
  request: DataCallbackRequestEcho; // final values used when submitted
  finalRequestedInfo?: RequestedInfo; // final user info used (if preSign requested info)
  callsId: string; // EIP-5792 submission result identifier
};

Service responses (service → wallet):

// Field-level validation errors for preSign
type DataCallbackErrors = {
  email?: string;
  phoneNumber?: { country?: string; number?: string };
  physicalAddress?: {
    address1?: string;
    address2?: string;
    city?: string;
    state?: string;
    postalCode?: string;
    country?: string;
  };
  name?: { firstName?: string; lastName?: string };
  onchainAddress?: string;
};

// Mutation-only for preSign; body is ignored for other events
type DataCallbackPreSignResponse = {
  request: {
    calls: {
      to: `0x${string}`;
      data?: `0x${string}`;
      value?: `0x${string}`;
    }[]; // updated (or original) calls
    capabilities?: Record<string, unknown>; // updated (or original) capabilities (MUST NOT modify dataCallback)
    chainId?: `0x${string}`; // updated (or original) chain id
  };
  errors?: DataCallbackErrors; // optional validation messages per field
};

Notes:

  • For initiated and postSign, responses are acknowledgment-only: wallets MUST ignore any response body and MUST NOT apply mutations.
  • For preSign, services MAY return errors to indicate field-level validation issues. Wallets SHOULD prompt users to correct fields and MAY re-invoke the preSign event after user correction.
  • For preSign, services MUST return either original or updated request fields (calls, capabilities, chain id). If no updates are required, services SHOULD return original values.
  • Services MAY modify capabilities during preSign, but they MUST NOT modify the dataCallback capability. I.e., the dataCallback capability value MUST stay constant throughout the request flow.
  • Wallets SHOULD retry non-2xx responses for initiated and postSign up to 3 retries using exponential backoff with jitter (e.g., ~1s, ~3s, ~9s; cap 10s). Wallets MUST NOT retry preSign automatically; they SHOULD retry only after user input changes or service availability is restored.

Wallet Behavior

  • Wallets MUST indicate support for this capability per chain in their wallet_getCapabilities response.
  • When dataCallback.events includes initiated, wallets SHOULD POST the initiated event promptly after receiving wallet_sendCalls with dataCallback, before any signing UI.
  • When dataCallback.events includes preSign, wallets MUST collect the requested information, POST the preSign event before finalizing the user operation, and honor any returned mutations. If errors are present, wallets MUST NOT let users proceed until errors are resolved or the user cancels.
  • Wallets MUST only send keys in requestedInfo that were requested and available (after user consent). If the user intentionally leaves an optional field blank, wallets MUST omit it from requestedInfo.
  • Wallets MUST pass the current calls, capabilities, and chainId in each event payload and, for preSign only, MUST accept updated calls/capabilities/chainId from the response.
  • When dataCallback.events includes postSign, wallets SHOULD POST the postSign event after the user has approved and the request has been submitted.
  • When dataCallback.events includes postSign, wallets SHOULD POST the postSign event after the user has approved and the request has been submitted, and MUST include the resulting callsId as returned by EIP-5792.
  • When dataCallback.events includes postSign, wallets SHOULD POST the postSign event after the user has approved and the request has been submitted, and MUST include the resulting callsId as returned by EIP-5792. If a preSign event with requests occurred, wallets MUST include finalRequestedInfo mirroring the capabilities.dataCallback augmentation returned from wallet_sendCalls.
  • Wallets MUST reject dataCallback requests that violate the constraints above (e.g., missing events, unknown event type, requests provided outside preSign, non-HTTPS callbackURL).

Wallet → App: Final user information in wallet_sendCalls response

If a dataCallback capability is present in the request, wallets MUST include the final user information they used under capabilities.dataCallback in the wallet_sendCalls response. The value MUST reflect the final set of keys used (respecting omitted optional fields).

type WalletSendCallsResponseAugmentation = {
  capabilities?: {
    dataCallback?: RequestedInfo; // final user info provided
  };
};
Example
{
  "id": "0x...",
  "capabilities": {
    "dataCallback": {
      "email": "hello@example.com"
    }
  }
}

wallet_getCapabilities Response

type DataCallbackCapabilitySupport = {
  supported: boolean;
};

Example:

{
  "0x1": { "dataCallback": { "supported": true } },
  "0x2105": { "dataCallback": { "supported": true } }
}

Rationale

  • HTTPS-only callback URLs minimize the risk of data interception or downgrade attacks.
  • Modeling the capability as explicit webhook events (initiated, preSign, postSign) aligns with familiar integration patterns and clarifies when mutation is allowed (only during preSign).
  • The request/response shapes are narrowly scoped to the data types wallets commonly manage, and the transformation rules avoid leaking internal profile constructs.
  • onchainAddress is not meaningfully optional. Once a user submits a transaction, the app can infer the user's onchain address by polling the resulting callsId and associated receipt; marking it optional would not prevent its disclosure.

Backwards Compatibility

  • The capability is additive to EIP-5792 and does not change existing method semantics.

Security Considerations

  • callbackURL MUST be HTTPS. Wallets MUST validate the scheme.
  • Apps are encouraged to use a backend they control as the callbackURL and proxy to third-party services if needed, avoiding leakage of service API keys.
  • Wallets MUST minimize the requestedInfo to only what was requested and consented to by the user and MUST only include requestedInfo in the preSign event.
  • Services SHOULD validate inputs and return clear field-level errors for preSign rather than failing the entire request.
  • For initiated and postSign, wallets MAY retry delivery up to 3 times on non-2xx responses (exponential backoff with jitter). Services SHOULD tolerate duplicate deliveries that can result from retries (no idempotency guarantee is provided by this spec).
  • Event context is application-defined and opaque to wallets. Services MUST treat context as untrusted input and validate/sanitize accordingly.

Privacy Considerations

  • Wallets SHOULD provide UI to review and optionally omit optional fields. Omitted optional fields MUST NOT be sent.
  • Wallets MUST NOT include requestedInfo in initiated or postSign events.
  • Wallets MUST NOT include requestedInfo in initiated events. In postSign, wallets MUST include finalRequestedInfo only when a preSign with requests was performed.
  • Apps should avoid including personally identifiable information in context. If included, it is subject to the app's own privacy policies and data handling obligations.

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