Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAIP-282 - Browser Wallet Messaging Interface #282

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Changes from 16 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9ac3552
Merge pull request #7 from ChainAgnostic/main
pedrouid May 28, 2024
8d509b6
add caip
pedrouid May 30, 2024
f0fe3c2
add caip number, discussions and update created date
pedrouid May 30, 2024
b051f62
...
pedrouid May 30, 2024
e41796a
Update CAIPs/caip-282.md
pedrouid May 30, 2024
7a81a4c
Update CAIPs/caip-282.md
pedrouid May 30, 2024
9fe4147
update event names
pedrouid May 30, 2024
3ef35dd
Merge branch 'browser-wallet-messaging-interface' of https://github.c…
pedrouid May 30, 2024
6ac99d4
Update CAIPs/caip-282.md
pedrouid Jun 6, 2024
42c645c
Update CAIPs/caip-282.md
pedrouid Jun 6, 2024
4986b61
Update CAIPs/caip-282.md
pedrouid Jun 6, 2024
0869e0d
Update CAIPs/caip-282.md
pedrouid Jun 6, 2024
f076f22
add missing sections
pedrouid Jun 6, 2024
eb13968
add test cases
pedrouid Jun 6, 2024
1cb66fd
tweak
pedrouid Jun 6, 2024
46c13a6
Update CAIPs/caip-282.md
pedrouid Jun 6, 2024
d2254ba
Update CAIPs/caip-282.md
pedrouid Jun 7, 2024
9f5029f
Update CAIPs/caip-282.md
pedrouid Jun 8, 2024
d0562ea
Apply suggestions from code review
pedrouid Jun 8, 2024
2eb5f0c
clean up and introduction of CAIP-217 scopes
pedrouid Jun 8, 2024
19a443c
WIP
pedrouid Jun 21, 2024
f2389e8
move to jsonrpc
pedrouid Jun 24, 2024
b6ced5c
add separate caips for extensions and iframes
pedrouid Jun 26, 2024
630a3e1
dispatchEvent and postMessage
pedrouid Jun 26, 2024
c2b6ff0
update dates
pedrouid Jun 26, 2024
3ea7bde
Update CAIPs/caip-282.md
pedrouid Jul 2, 2024
374e710
Update CAIPs/caip-282.md
pedrouid Jul 2, 2024
0f17dd3
Update CAIPs/caip-282.md
pedrouid Jul 2, 2024
c1e833f
Update CAIPs/caip-282.md
pedrouid Jul 2, 2024
02f119f
add extensionId for externally_connectable
adonesky1 Aug 2, 2024
bc49428
Merge pull request #8 from adonesky1/ad/caip-294-externally_connectable
pedrouid Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
385 changes: 385 additions & 0 deletions CAIPs/caip-282.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,385 @@
---
caip: 282
title: Browser Wallet Messaging Interface
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
author: Pedro Gomes (@pedrouid)
discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/282
status: Draft
type: Standard
created: 2023-05-30
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
requires: 2, 10, 25, 27, 222, 275
---

## Simple Summary

Standardized messaging interface for browser wallets.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Abstract

When interfacing with decentralized applications users install browser wallets to manage their blockchain accounts which apps require to sign messages and transactions. Leveraging existing browser messaging APIs these are are used for messaging to initiate a dapp-wallet connection in a browser environment.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Motivation

Currently, in order for decentralized applications to be able to support all users they need to support all browser wallet APIs. Similarly, in order for browser wallets to support all decentralized applications they need to support all APIs. This is not only complicated but also results in a larger bundle size of applications.

Users are already installing a wallet application on their devices and given its presence in the browser environment it should be unnecessary for application developers to also have to install extra software to support these wallet providers. This situation is only present due to lack of standardization and interoperability between the interfaces and also discovery mechanisms for different wallet providers.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

pedrouid marked this conversation as resolved.
Show resolved Hide resolved
This results not only in a degraded user experience but also increases the barrier to entry for new wallet providers as users are incentivized to use more popular wallet providers that are more widely supported in more applications.

This situation is further aggravated by differences between blockchain networks such as Ethereum, Cosmos, Solana, Tezos, etc. While some solutions amttept to solve this such as WalletConnect, EIP-6963, Solana Wallet Protocol, etc. They are not covering all wallets and are not chain-angostic.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

In this proposal, we present a solution that focused on optimizing interoperability for multiple Wallet Providers and fostering fairier competition by reducing the barriers to entry for new Wallet Providers, along with enhancing the user experience across all blockchain networks.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC-2119].

### Definitions

Wallet Provider: A user agent that manages accounts and facilitates transactions with a blockchain.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

Decentralized Application (DApp): A web page that relies upon one or many Web3 platform APIs which are exposed to the web page via the Wallet.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(another one - dapp vs DApp vs dApp vs derp)


Blockchain Library: A library or piece of software that assists a DApp to interact with blockchain and interface with the Wallet.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

### Messaging APIs

The browser exposes two APIs that can be used for messaging across different parts of the stack. Using `window.addEventListener` and `window.postMessage` enables communication between browser window, iframes and extensions.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

This provides the foundation for any wallet provider to interface with a decentralized application using a blockchain library which implements this standard.

There are different loading times that can be affected by multiple factors which makes it non-deterministic to publish and listen messages from different sources within the browser.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

#### Discovery

Both wallet providers and blockchain libraries must listen to incoming messages that might be published after their initialization. Additionally both wallet providers and blockchain libraries must publish a message to both announce themselves and their intent to connect, respectively.

Here is the expected logic from the blockchain library:

```js
const wallets = {};

// blockchain library starts listening on init
window.addEventListener("caip282:announceWallet", (event) => {
// when an announce message was received then the library can index it by uuid
wallets[event.data.uuid] = event.data;
});

// blockchain library publishes on init
window.postMessage("message", {
event: "caip282:promptWallet",
data: {
// if the blockchain library supports CAIP-275 then it can include a name
name: "", // optional
},
});
```

Here is the expected logic from the wallet provider:

```js
// wallet provider sets data on init
const data = {
uuid: "";
name: "";
icon: "";
rdns: "";
}


// wallet provider publishes on init
window.postMessage("message", {
event: "caip282:announceWallet",
data,
});


// wallet providers starts listenning on init
window.addEventListener("caip282:promptWallet", (event) => {
// when a prompt message was received then the wallet will announces again
window.postMessage("message", {
event: "caip282:announceWallet",
data,
});
});
```

Whenever a new wallet provider is discovered the blockchain library would index them in order for the decentralized application to display them and prompt the user for selecting their wallet of choice for this connection. Each wallet announced will share the following data:

```typescript
interface caip282WalletData {
uuid: string;
name: string;
icon: string;
Copy link

@kewde kewde Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we specify the allowed formats and URI?

export type WalletIcon = `data:image/${'svg+xml' | 'webp' | 'png' | 'gif'};base64,${string}`;

Is what wallet standard recommends, I think the data URIs should be the mandatory format.

Do we want to allow or specifically forbid remote images?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually yes... that is missing here!

We definitely want to forbid remote images

rdns: string;
}
```

The parameters `name` and `icon` are used to display to the user to be easily recognizable while the `rdns` and `uuid` are only used internally for de-duping while they must always be unique, the `rdns` will always be the same but `uuid` is ephemeral per browser session.

#### Handshake

After the wallet has been selected by the user then the blockchain library MUST publish a message to share its intent to establish a connection. This can be either done as a [CAIP-25][caip-25] request or [CAIP-222][caip-222] authentication.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "can be either" as in MUST be one of? Or MAY be one, the other, some future third thing, or some fourth thing that already exists but we discourage? Probably worth being very explicit here.

Another complication that comes to mind is fallbacks, i.e. if a wallet prefers 222, but falls back to 25 after 222 times out or otherwise fails; probably worth expressing the intent to connect as a sequence of requests and responses eventually ending in success or failure rather than as a single message, if that sidesteps the enumeration of valid message types.


The communication will use the `uuid` shared by the initial wallet provider announcement payload which the wallet provider will be listening to for any incoming requests and consequently the blockchain library will also use for publishing messages. The same will happen again the other way around but vice-versa where the wallet provider will be the blockchain library will be listening to for any incoming responses and consequently the wallet provider will also use for publishing messages.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

Here is the expected logic from the blockchain library:

```js
// blockchain library listens for responses
window.addEventListener("caip282:respond:<wallet_provider_uuid>", (event) => {
console.log(event.data);
});

// blockchain library publishes for requests
window.postMessage("message", {
event: "caip282:request:<wallet_provider_uuid>",
data: { ... },
});
```

Here is the expected logic from the wallet provider:

```js
// wallet provider listens for request
window.addEventListener("caip282:request:<wallet_provider_uuid>", (event) => {
console.log(event.data);
});

// wallet provider publishes for reponses
window.postMessage("message", {
event: "caip282:respond:<wallet_provider_uuid>",
data: { ... },
});
```

#### Signing

This same channel uuid can then be used for a connected session using the [CAIP-27][caip-27] which then would use the sessionId from the established connection to identify incoming payloads that need to be respond and also which chainId is being targetted.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

#### UUIDs

The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users.
Copy link

@kewde kewde Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we explain why are uuid's crucial?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UUID is local and ephemeral

RDNS is global and permanent

The two identifiers together allow wallets to be identifiable but also de-duplicated if necessary

Either one by itself would not meet those requirements


A wallet provider MUST generate UUIDs always distinctly for each webpage loaded and they must not be re-used without a session being established between the application and the wallet with the user's consent.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

A UUID can be re-used as a sessionId if and only if the CAIP-25 or CAIP-222 has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

Once established the UUID is used as sessionId also for the CAIP-27 payloads which can verify that incoming messages are being routed through the appropriate channels.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Rationale

Browser wallets differentiate themselves because they can be installed by users without the application developer require any further integration. Therefore we optimize for a messaging interface that leverages the two-way communication available to browser wallets to make themselves discoverable and negotiate a set of parameters that enable not only easy human readability with a clear name and icon but also machine-readability using strong identifiers with uuid and rdns.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

The choice for using window.postMessage is motivated by expanding the range of wallet providers it can support which would include not only browser extensions that can alternatively use window.dispatchEvent but instead it would cover also Inline Frames, Service Workers, Shared Workers, etc.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

The use of UUID for message routing is important because while RDNS is useful for identifying the wallet provider, it causes issues when it comes to the session management of different webpages connected to the same wallet provider or even managing stale sessions which can be out-of-sync. Since UUID generation is derived dynamically on page load then wallet providers can track these sessions more granularly rather than making assumptions around webpage URL and RDNS relationship.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

The existing standards around provider authorization (CAIP-25) and wallet authentication (CAIP-222) are fundamental to this experience because they create clear intents for a wallet to "connect" with a webpage url after it's been discovered. This standard does not enforce either one but strongly recommends these standards as the preferred interface for connecting or authenticating a wallet.

Finally the use of CAIP-27 leverages the work above to properly target signing requests that are intended to be prompt to wallet users which will include a sessionId and chainId in parallel with the pre-established sessions using either CAIP-25 or CAIP-222
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Test Cases

Here is a test case where we demonstrate a scenario with logic from both a blockchain library and a wallet provider
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

Logic from the blockchain library:

```js
// 1. blockchain library initializes by listening to announceWallet messages and
// also by posting a prompt message
const wallets = {};
window.addEventListener("caip282:announceWallet", (event) => {
wallets[event.data.uuid] = event.data;
});
window.postMessage("message", { event: "caip282:promptWallet", data: {} });

// 2. User presses "Connect Wallet" and the library display the discovered wallets

// 3. User selects a Wallet with UUID = "350670db-19fa-4704-a166-e52e178b59d2" and
// blockchain library will send a CAIP-25 request to establish a wallet connection
let session = {};
window.addEventListener("caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", (event) => {
if (event.data.error) throw new Error(event.data.error.message);
session = event.data.result;
});
window.postMessage("message", {
event: "caip282:request:350670db-19fa-4704-a166-e52e178b59d2",
data: {
id: 1,
jsonrpc: "2.0",
method: "provider_authorize",
params: {
optionalScopes: {
eip155: {
scopes: ["eip155:1", "eip155:10"],
methods: ["eth_sendTransaction", "personal_sign"],
notifications: ["accountsChanged", "chainChanged"],
},
},
sessionProperties: {
expiry: "2024-06-06T13:10:48.155Z",
},
},
},
});

// 4. After the response was received by the blockchain library from the wallet
// provider then the session is established with a sessionId matchin the UUID
// thus signing requests can be using a CAIP-27 request to the wallet user
let result = {};
window.addEventListener("caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", (event) => {
if (event.data.error) throw new Error(event.data.error.message);
result = event.data.result;
});
window.postMessage("message", {
event: "caip282:request:350670db-19fa-4704-a166-e52e178b59d2",
data: {
{
id: 2,
jsonrpc: "2.0",
method: "provider_request",
params: {
sessionId: "350670db-19fa-4704-a166-e52e178b59d2",
scope: "eip155:10",
request: {
method: "eth_sendTransaction",
params: [
{
type: "0x2",
nonce: "0x01",
value: "0x00",
maxFeePerGas: "0x9143798a4",
maxPriorityFeePerGas: "0x59682f00",
from: "0x43e3ca49c7be4f429abce408da6b738f879d02a0",
to: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
data: "0xa9059cbb000000000000000000000000677d6d2747955ecf1e9fad3521d29512fb599e7b0000000000000000000000000000000000000000000000000de0b6b3a7640000",
}
]
}
}
}
},
});

```

Logic from the wallet provider:

```js
// 1. wallet provider sets their wallet data and then listens to promptWallet message
// and also immediatelly posts a message with the wallet data as announceWallet type
const data = {
uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2"
name: "Example Wallet",
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
rdns: "com.example.wallet";
}
window.addEventListener("caip282:promptWallet", (event) => {
// when a prompt message was received then the wallet will announces again
window.postMessage("message", {
event: "caip282:announceWallet",
data,
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
});
});
window.postMessage("message", {
event: "caip282:announceWallet",
data,
});

// 2. User presses "Connect Wallet" on the application webpage which will select UUID

// 3. Wallet provider receives a CAIP-25 request to establish a wallet connection
// prompts the user to approve and once its approved it can respond back to app
// wallet provider listens for request
const request = {}
window.addEventListener("caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => {
request = event.data
});
// wallet provider publishes for reponses
window.postMessage("message", {
event: "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2",
data: {
id: request.id, // 1
jsonrpc: "2.0",
result: {
sessionId: "350670db-19fa-4704-a166-e52e178b59d2",
sessionScopes: {
eip155: {
scopes: ["eip155:1", "eip155:10"],
methods: ["eth_sendTransaction", "personal_sign"],
notifications: ["accountsChanged", "chainChanged"],
accounts: [
"eip155:1:0x43e3ca49c7be4f429abce408da6b738f879d02a0",
"eip155:10:0x43e3ca49c7be4f429abce408da6b738f879d02a0"
]
},
},
sessionProperties: {
expiry: "2024-06-06T13:10:48.155Z",
}
}
},
});

// 4. Once the connection is established then the Wallet provider can receive
// incoming CAIP-27 requests which will be prompted to the user to sign and
// once signed the response is sent back to the dapp with the expected result
const request = {}
window.addEventListener("caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => {
request = event.data
});
// wallet provider publishes for reponses
window.postMessage("message", {
event: "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2",
data: {
id: request.id, // 2
jsonrpc: "2.0",
result: "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
},
});
```

## Security Considerations

The advantage of using window.postMessage over existing standards that leverage window.dispatchEvent is the prevention of prototype pollution but that still does not mean that there aren't existing attacks that must be considered:
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

### Wallet Imitation and Manipulation

Application developers are expected to actively detect for misbehavior of properties or functions being modified in order to tamper with or modify other wallets. One way this can be easily achieved is to look for when the uuid property within two Wallet Data objects match. Applications and Libraries are expected to consider other potential methods that the Wallet Data objects are being tampered with and consider additional mitigation techniques to prevent this as well in order to protect the user.

### Prevent SVG Javascript Execution

The use of SVG images introduces a cross-site scripting risk as they can include JavaScript code. This Javascript executes within the context of the page and can therefore modify the page or the contents of the page. So when considering the experience of rendering the icons, DApps need to take into consideration how they’ll approach handling these concerns in order to prevent an image being used as an obfuscation technique to hide malicious modifications to the page or to other wallets.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Privacy Considerations

Any form of wallet discoverability must alwasys take in consideration wallet fingerprinting that can happen by malicious webpages or extensions that attempt to capture user information. Thus wallet providers can abstain from publishing announceWallet messages on every page load and wait for incoming promptWallet messages. Yet this open the possiblity for race conditions where wallet providers could be initialized after the promptWallet message was published therefore be undiscoverable. It is recommended that if wallet providers do offer this more "private connect" feature that is only enabled optionally by users rather than set by default.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

## Backwards Compatibility

It's important to note that existing blockchain ecosystems already have standards that overlap with the scope of this standard and backwards-compatibility must be considered for a smooth adoption by both wallet and application developers.

For EIP155 (Ethereum) ecosystem there are already interfaces for discoverability of browser wallets through either legacy window.ethereum or EIP-6963 events. These existing mechanisms should be supported in parallel in order to receive incoming requests either through this new messaging interface or legacy ones.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

Similarly the Solana and BIP122 (Bitcoin) ecosystems have used similar patterns around window.solana and window.bitcoin respectively plus the wallet-standard events. Yet these can also be supported in parallel without conflict with this new messaging interface.
pedrouid marked this conversation as resolved.
Show resolved Hide resolved

The Wallet Data exposed in this messaging interface is also compatible with EIP-6963 events and wallet-standard events therefore wallet providers can re-use the same identifiers and assets already being used in these existing integrations.

## Links

- [CAIP-2][caip-2] - Blockchain ID Specification
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
- [CAIP-10][caip-10] - Account ID Specification
- [CAIP-27][caip-27] - Blockchain ID Specification
- [CAIP-25][caip-25] - Blockchain ID Specification
- [CAIP-222][caip-222] - Account ID Specification

[caip-2]: https://chainagnostic.org/CAIPs/caip-2
pedrouid marked this conversation as resolved.
Show resolved Hide resolved
[caip-10]: https://chainagnostic.org/CAIPs/caip-10
[caip-27]: https://chainagnostic.org/CAIPs/caip-27
[caip-25]: https://chainagnostic.org/CAIPs/caip-25
[caip-222]: https://chainagnostic.org/CAIPs/caip-222
[caip-275]: https://chainagnostic.org/CAIPs/caip-275

## Copyright

Copyright and related rights waived via [CC0](../LICENSE).