CAIP-25 proposes a standardized way to describe and interact with blockchain assets across different blockchain networks. It mainly focuses on token identifiers, which are used to uniquely identify tokens (like cryptocurrencies and digital assets) across multiple blockchains.
CAIP-25 defines an authorization procedure for a chain agnostic provider to interface with a wallet as part of their initialization and/or "handshake" protocol.
This proposal has the goal to define a standard procedure for decentralized applications to interface with chain agnostic cryptocurrency wallets and other user agents which govern identities (including accounts) in multiple cryptographic systems. It defines a lightweight protocol for negotiating and persisting authorizations during a session managed by a provider construct.
The motivation comes from the lack of standardization across blockchains to expose accounts and define the expected JSON-RPC methods to be used by an application through a provider connecting to a signer or other user agent.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" written in uppercase in this document are to be interpreted as described in [RFC 2119][]
The session is proposed by a caller and the response by the respondent is used as the baseline for an ongoing session between the two parties.
sessionId (an entropic identifier), the properties and authorization scopes that make up the session should be persisted and tracked over the life of the session by both parties in a discrete data store.sessionId in its initial response, the wallet MUST persist and track the properties and authorization scopes that make up the session.
The caller is not expected to persist session data or even a sessionId.
Note that wallets NOT returning sessionIds MUST implement additional methods and notifications to handle the full lifecycle of the session:
wallet_getSession to enable the caller to query for the current status of the session at any time.wallet_revokeSession to explicitly end the sessionwallet_sessionChanged to notify caller of updated session authorizations.After a session is established between wallet and caller, subsequent wallet_createSession calls can be used to update the properties and authorization scopes of the session.
sessionId is returned in the initial wallet_createSession response, subsequent wallet_createSession calls either:
sessionId on the root of the request meaning this request is intended to modify that session, orsessionId, in which case a new session is created - the respondent generates a new sessionId and sends it with the success response - and the previous session dangles in parallel (until its expiration, if applicable), though maintaining concurrent sessions is discouraged (see Security Considerations).sessionId in its initial response, subsequent wallet_createSession calls overwrite the previous singular session between caller and wallet.When a user wishes to update the authorizations of an active session from within the wallet, the wallet should notify the caller of the changes with a wallet_sessionChanged notification.
If a connection is initially established without a sessionId and the wallet later implements sessionId support, the wallet can revoke the single session and notify the caller via wallet_sessionChanged. When the caller seeks to re-establish the session via wallet_createSession, the wallet should return a sessionId in the response.
When a caller wishes revoke an active session, it can do so by calling wallet_revokeSession.
sessionId is returned in the initial wallet_createSession response, the caller MUST call wallet_revokeSession with the supplied sessionId to revoke that session.sessionId in its initial response, a call to wallet_revokeSession revokes the single active session between caller and wallet.For more detail on the lifecycle and management of sessions with and without sessionIds, see the informational CAIP-316.
Initial and ongoing authorization requests are grouped into two top-level objects containing keyed scopeObjects, named requiredScopes and optionalScopes
respectively.
Each scopeObject in either parent object MUST be keyed uniquely within its parent, but these keys CAN appear in both
(i.e., additional properties of an authorization target in requiredScopes may be requested in a separate scopeObject with the same key in the optionalScopes array).
Each scopeObject in these parent ...Scopes objects can be keyed to a specific CAIP-2 network identifier, or to an entire CAIP-104 namespace.
scopeObjects keyed to an entire CAIP-104 namespace SHOULD contain a non-empty references array to be actionable, making them functionally equivalent to a series of identical scopeObjects, each keyed to one of the members of references expressed as a CAIP-2 scope.
An empty or absent references array SHOULD NOT be interpreted as a namespace-wide authorization (i.e. authorization for ANY network therein), but rather as a null authorization of 0 specified referencess within that namespace.
(See CAIP-217 for more details on the structure of the typed objects included in these ...Scopes objects.)
The distinction between requiredScopes and optionalScopes is ultimately semantic, since a wallet may still choose to establish a connection authorizing a subset of requested networks or requested capabilities from each; the primary function of the distinction is to offer callers a mechanism for signaling which scopes they consider primary and which they consider secondary to their request, in order to better inform the authorization logic of the respondent.
If a connection is being rejected, whether on the basis of end-user input or on the basis of evaluating requiredScopes against available capabilities, the respondent SHOULD choose its response based on trust:
e.g., one or more specific failure states MAY be sent (see #### failure states below) for trusted counterparties, but an undefined response (or no response, depending on implementation) MAY also be sent to prevent incentivizing unwanted requests and to minimize the surface for fingerprinting of public web traffic (See Privacy Considerations below).
After parsing and authorizing separately all the networks and capabilities within each, a respondent establishes a connection by returning a success response that organizes all authorized features of each authorized scope in a single unified object of scopeObjects called sessionScopes.
In the case of identically-keyed scopeObjects appearing in both top-level objects in the request (requestedScopes and optionalScopes), the identically-scoped objects MUST be merged in the response, since sessionScopes MUST NOT contain redundant keys (see examples below).
However, respondents MUST NOT restructure scopes (e.g., by folding properties from a CAIP-2-keyed, chain-specific scope object into a CAIP-104-keyed, namespace-wide scope object) as this may introduce ambiguities (See Security Considerations below).
The application would interface with a provider to authorize that provider with a given set of parameters by calling the following JSON-RPC request
Example:
The JSON-RPC method is labeled as wallet_createSession and its params object contains "requiredScopes" and/or "optionalScopes" objects populated with CAIP-217 "scope objects" keyed to CAIP-217 scope strings.
requiredScopes object MUST contain 1 or more scopeObjects, if present.optionalScopes object MUST contain 1 or more scopeObjects, if present.A third object is the scopedProperties object, which also MUST contain 1 or more objects if present.
Each object should be keyed to the scope of a sessionScopes member to which it corresponds.
All properties of each object in scopedProperties MUST be interpreted by the respondent as proposals or declarations rather than as requirements.
In addition to making additional properties of or metadata about the corresponding sessionScopes member explicit, they can also annotate, support, or extend the negotiation of scope proposals (e.g., providing connection information about unfamiliar scopes, or which accounts to expose to each).
A fourth object, sessionProperties, is optional and its shape undefined.
It is intended for metadata or additional information not bound to any specific authorization scope, but made "global" to the connection.
The respondent SHOULD ignore and drop from its response any properties not defined in this document or in another CAIP document extending this protocol which the respondent has implemented in its entirety;
similarly, the requiredScopes, optionalScopes, and sessionScopes objects returned by the respondent SHOULD contain only valid CAIP-217 objects, and properties not defined in CAIP-217 SHOULD also be dropped from each of those objects.
The same absolute security posture is not expected for the metadata objects scopedProperties and sessionProperties, but caution is still recommended for such extensions:
callers and respondents alike SHOULD allow for their counterparties dropping or ignoring unfamiliar members from either.
When a sessionId is returned with the initial success response, requesting applications and respondents alike are expected to manage state for the connection, including scopedProperties and sessionProperties, and that same sessionId should be added to the wallet_createSession request to update the associated session.
When no sessionId is included in an initial success response, a caller does not need to maintain sessionId state and can assume the extension methods defined in CAIP-311 and CAIP-312 are available for refreshing and updating the session directly.
See CAIP-316 for more on lifecycle management.
if multiple concurrent connections are allowed, callers are expected to track, persist and identify them separately by the unique sessionId returned initially.
The wallet can respond to this method with either a success result or an error message.
The successful result MAY contain a string (keyed as sessionId with a value conformant to CAIP-171). As described above, if a sessionId is returned in the response, the caller should persist and track the properties and authorization scopes associated with this sessionid. If the wallet does not return a sessionId in the response, the connection will only consist of one session at a time, the contents of which are always retrievable for the caller via wallet_getSession.
The successful result MUST contain an object called sessionScopes which contains 1 or more scopeObjects.
scopeObjects and all, none, or some of the optional scopeObjects (at the discretion of the provider) MUST be included if successful.accounts array,
containing 0 or more CAIP-10-conformant accounts authorized for the session
and valid in that scope. Additional constraints on the accounts authorized for a given session MUST be applied conformant to the namespace's CAIP-10 profile, if one has been specified.A scopedProperties object MAY also be present, each member of which corresponds to exactly 1 sessionScope.
This is intended for expressing connection-specific or non-standardized extensions to sessionScope.
Each object in scopedProperties MUST be keyed to a scopeString, and SHOULD correspond to a sessionScopes entry with the same key.
There are no type, depth, or shape constraints on the contents of each property in scopedProperties.
If an object in scopedProperties is keyed to a scopeString not currently authorized for the session, it SHOULD be ignored.
A sessionProperties object MAY also be present, with no protocol-wide shape constraints or semantics assumed.
This is intended for expressing metadata about the CAIP-25 connection and accessible to ALL sessionScopes equally.
There are no type, depth, or shape constraints on the contents of sessionProperties.
An example of a successful response follows:
The response MUST NOT be a JSON-RPC success result in any of the following failure states.
Unless the dapp is known to the wallet and trusted, the generic/undefined error response,
is RECOMMENDED for any of the following cases:
More informative error messages MAY be sent in trusted-counterparty circumstances, although extending this trust too widely may contribute to widespread fingerprinting and analytics which corrode herd privacy (see Privacy Considerations below). The core error messages over trusted connections are as follows:
The valid error messages codes are the following:
Regardless of caller trust level, the following error responses can reduce friction and user experience problems in the case of malformed requests.
chainId two ways
Note: respondents SHOULD to implement support for core RPC Documents per each supported namespace to avoid sending error messages 5201 and 5202 in cases where 0, 5101 or 5102 would be more appropriate. Failure to do so may leak versioning or feature-completeness information to a malicious or fingerprinting caller.
The crucial security function of a shared session negotiated and maintained by a series of CAIP-25 calls is to reduce ambiguity in authorization. This requires a potentially counterintuitive structuring of the building-blocks of a Chain-Agnostic session into scopes at the "namespace-wide" (CAIP-104) or at the "chain-specific" (CAIP-2) level; for this reason, requests and responses are structures as objects full of objects keyed to these scopes, formatted either as a CAIP-104 scheme OR as a full CAIP-2. While internal systems are free to translate this object into other structures, preserving it in the CAIP-25 interface is crucial to the unambiguous communication between caller and respondent about what exact authorization is granted.
One major risk in browser-based or HTTP-based communications is "fingerprinting risk", i.e. the risk that public or intercepted traffic can be used to deanonymize browsers and/or wallets deductively based on response times, error codes, etc. To minimize this risk, and to minimize the data (including behavioral data) leaked by responses to potentially malicious CAIP-25 calls, respondents are recommended to ignore calls
"Ignoring" these calls means responding to all three in a way that is indistinguishable to a malicious caller or observer which might deduce information from differences in those responses (including the time taken to provide them). Effectively, this means allowing requests in all three cases to time out even if the end-user experience might be better served by differentiating them, particularly in complex multi-party architectures where parties on one side of this interface need to have a shared understanding of why a request did not receive a response. At scale, however, better user experiences in a single architecture or context can contribute to a systemic erosion of anonymity.
Given this "silent time out" behavior, the best strategy to ensure good user experience is not to request too many properties in the initial establishment of a session and to iteratively and incrementally expand session authorization over time. This also contributes to a more consentful experience overall and encourages progressive trust establishment across complex architectures with many distinct actors and agents.
Another design pattern that accommodates the "silent time out" behavior is minor updates to the session. For example, a caller sending a request identical to a previous request (or a previous response) except for a new session expiry further in the future could expect one of exactly three responses:
-- 2024-07-29: added lifecycle management methods and notification for single session connections, see CAIP-316 for equivalence chart and diagrams
sessionIdsscopeObjectswallet_revokeSession Specificationwallet_getSession Specificationwallet_sessionChanged SpecificationCopyright and related rights waived via CC0.