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:
eventsMUST be present and non-empty.- Each event's
typeMUST be one of exactly"initiated","preSign", or"postSign". Wallets MUST reject unknown event names. callbackURLMUST be a valid HTTPS URL.requestsis only permitted onpreSignevents and MUST be a non-empty array without duplicatetypeentries.- Each
optionalMUST be a boolean if present (defaults to false). - If
typeisonchainAddress, it MUST NOT be marked optional. - If a
preSignevent withrequestsis desired, apreSignevent MUST be present inevents(there is no default).eventsMUST 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
initiatedandpostSign, responses are acknowledgment-only: wallets MUST ignore any response body and MUST NOT apply mutations. - For
preSign, services MAY returnerrorsto indicate field-level validation issues. Wallets SHOULD prompt users to correct fields and MAY re-invoke thepreSignevent 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 thedataCallbackcapability. I.e., thedataCallbackcapability value MUST stay constant throughout the request flow. - Wallets SHOULD retry non-2xx responses for
initiatedandpostSignup to 3 retries using exponential backoff with jitter (e.g., ~1s, ~3s, ~9s; cap 10s). Wallets MUST NOT retrypreSignautomatically; 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_getCapabilitiesresponse. - When
dataCallback.eventsincludesinitiated, wallets SHOULD POST theinitiatedevent promptly after receivingwallet_sendCallswithdataCallback, before any signing UI. - When
dataCallback.eventsincludespreSign, wallets MUST collect the requested information, POST thepreSignevent before finalizing the user operation, and honor any returned mutations. Iferrorsare present, wallets MUST NOT let users proceed until errors are resolved or the user cancels. - Wallets MUST only send keys in
requestedInfothat were requested and available (after user consent). If the user intentionally leaves an optional field blank, wallets MUST omit it fromrequestedInfo. - Wallets MUST pass the current
calls,capabilities, andchainIdin each event payload and, forpreSignonly, MUST accept updatedcalls/capabilities/chainIdfrom the response. - When
dataCallback.eventsincludespostSign, wallets SHOULD POST thepostSignevent after the user has approved and the request has been submitted. - When
dataCallback.eventsincludespostSign, wallets SHOULD POST thepostSignevent after the user has approved and the request has been submitted, and MUST include the resultingcallsIdas returned by EIP-5792. - When
dataCallback.eventsincludespostSign, wallets SHOULD POST thepostSignevent after the user has approved and the request has been submitted, and MUST include the resultingcallsIdas returned by EIP-5792. If apreSignevent withrequestsoccurred, wallets MUST includefinalRequestedInfomirroring thecapabilities.dataCallbackaugmentation returned fromwallet_sendCalls. - Wallets MUST reject
dataCallbackrequests that violate the constraints above (e.g., missingevents, unknown event type,requestsprovided outsidepreSign, non-HTTPScallbackURL).
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 duringpreSign). - The request/response shapes are narrowly scoped to the data types wallets commonly manage, and the transformation rules avoid leaking internal profile constructs.
onchainAddressis not meaningfully optional. Once a user submits a transaction, the app can infer the user's onchain address by polling the resultingcallsIdand 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
callbackURLMUST be HTTPS. Wallets MUST validate the scheme.- Apps are encouraged to use a backend they control as the
callbackURLand proxy to third-party services if needed, avoiding leakage of service API keys. - Wallets MUST minimize the
requestedInfoto only what was requested and consented to by the user and MUST only includerequestedInfoin thepreSignevent. - Services SHOULD validate inputs and return clear field-level errors for
preSignrather than failing the entire request. - For
initiatedandpostSign, 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
contextis application-defined and opaque to wallets. Services MUST treatcontextas 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
requestedInfoininitiatedorpostSignevents. - Wallets MUST NOT include
requestedInfoininitiatedevents. InpostSign, wallets MUST includefinalRequestedInfoonly when apreSignwithrequestswas 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
Copyright and related rights waived via CC0.
