From 8d509b66fd10a42d84c203b042af192570e51371 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 12:52:47 -0500 Subject: [PATCH 01/28] add caip --- CAIPs/caip-xxx.md | 198 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 CAIPs/caip-xxx.md diff --git a/CAIPs/caip-xxx.md b/CAIPs/caip-xxx.md new file mode 100644 index 00000000..db9cb614 --- /dev/null +++ b/CAIPs/caip-xxx.md @@ -0,0 +1,198 @@ +--- +caip: xxx +title: Browser Wallet Messaging Interface +author: Pedro Gomes (@pedrouid) +discussions-to: TODO +status: Draft +type: Standard +created: 2023-05-28 +requires: 2, 10, 25, 27, 222, 275 +--- + +## Simple Summary + +Standardized messaging for wallet interface in browser environments. + +## Abstract + +When interfacing with decentralized applications users install wallets in their browser 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. + +## Motivation + +Currently, decentralized applications are required to install a multitude of SDKs to support many different wallet providers to broaden its userbase with different wallets of choice. However this creates a big overhead for applications to support more and more proprietrary SDKs that increase its size and laso creates vendor lockin for major wallet providers that can allocate resources to reach more applications. + +Users are already installing a wallet applicationg in 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. + +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. + +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. + +## 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. + +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. + +Blockchain Library: A library or piece of software that assists a DApp to interact with blockchain and interface with the Wallet. + +### Messaging APIs + +The browser has available 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. + +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. + +#### 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("caipxxx: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: "caipxxx:promptWallet", + data: { + // if the blockchain library supports CAIP-275 then it can include a name in its prompt + 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: "caipxxx:announceWallet", + data, +}); + + +// wallet providers starts listenning on init +window.addEventListener("caipxxx:promptWallet", (event) => { + // when a prompt message was received then the wallet will announces again + window.postMessage("message", { + event: "caipxxx: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 CAIPXXXWalletData { + uuid: string; + name: string; + icon: string; + 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. + +#### Connection + +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. + +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. + +Here is the expected logic from the blockchain library: + +```js +// blockchain library listens for responses +window.addEventListener("caipxxx:respond:", (event) => { + console.log(event.data); +}); + +// blockchain library publishes for requests +window.postMessage("message", { + event: "caipxxx:request:", + data: { name: "test" }, +}); +``` + +Here is the expected logic from the wallet provider: + +```js +// wallet provider listens for request +window.addEventListener("caipxxx:request:", (event) => { + console.log(event.data); +}); + +// wallet provider publishes for reponses +window.postMessage("message", { + event: "caipxxx:respond:", + data: { name: "test" }, +}); +``` + +#### 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. + +## Rationale + +TODO + +## Test Cases + +TODO + +## Security Considerations + +TODO + +## Privacy Considerations + +TODO + +## Backwards Compatibility + +TODO + +## Links + +- [CAIP-2][caip-2] - Blockchain ID Specification +- [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 +[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). From f0fe3c27af56c393894bcb8e829d81040582fc5e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 12:54:04 -0500 Subject: [PATCH 02/28] add caip number, discussions and update created date --- CAIPs/{caip-xxx.md => caip-282.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename CAIPs/{caip-xxx.md => caip-282.md} (98%) diff --git a/CAIPs/caip-xxx.md b/CAIPs/caip-282.md similarity index 98% rename from CAIPs/caip-xxx.md rename to CAIPs/caip-282.md index db9cb614..98e43c58 100644 --- a/CAIPs/caip-xxx.md +++ b/CAIPs/caip-282.md @@ -1,11 +1,11 @@ --- -caip: xxx +caip: 282 title: Browser Wallet Messaging Interface author: Pedro Gomes (@pedrouid) -discussions-to: TODO +discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/282 status: Draft type: Standard -created: 2023-05-28 +created: 2023-05-30 requires: 2, 10, 25, 27, 222, 275 --- From b051f62250bc6738f26500c8ac5f994627508bf7 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 12:59:41 -0500 Subject: [PATCH 03/28] ... --- CAIPs/caip-282.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 98e43c58..711f1f89 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -135,7 +135,7 @@ window.addEventListener("caipxxx:respond:", (event) => { // blockchain library publishes for requests window.postMessage("message", { event: "caipxxx:request:", - data: { name: "test" }, + data: { ... }, }); ``` @@ -150,7 +150,7 @@ window.addEventListener("caipxxx:request:", (event) => { // wallet provider publishes for reponses window.postMessage("message", { event: "caipxxx:respond:", - data: { name: "test" }, + data: { ... }, }); ``` From e41796ac06d8da92f4e15045343ac2082e87976e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 13:34:27 -0500 Subject: [PATCH 04/28] Update CAIPs/caip-282.md Co-authored-by: Chris Smith <1979423+chris13524@users.noreply.github.com> --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 711f1f89..9fd1c976 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -19,7 +19,7 @@ When interfacing with decentralized applications users install wallets in their ## Motivation -Currently, decentralized applications are required to install a multitude of SDKs to support many different wallet providers to broaden its userbase with different wallets of choice. However this creates a big overhead for applications to support more and more proprietrary SDKs that increase its size and laso creates vendor lockin for major wallet providers that can allocate resources to reach more applications. +Currently, decentralized applications are required to install a multitude of SDKs to support many different wallet providers to broaden its userbase with different wallets of choice. However this creates a big overhead for applications to support more and more proprietrary SDKs that increase its size and also creates vendor lockin for major wallet providers that can allocate resources to reach more applications. Users are already installing a wallet applicationg in 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. From 7a81a4cc3b2e14e9680ccd5d87a67218799bded8 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 13:34:35 -0500 Subject: [PATCH 05/28] Update CAIPs/caip-282.md Co-authored-by: Chris Smith <1979423+chris13524@users.noreply.github.com> --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 9fd1c976..b60f2950 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -21,7 +21,7 @@ When interfacing with decentralized applications users install wallets in their Currently, decentralized applications are required to install a multitude of SDKs to support many different wallet providers to broaden its userbase with different wallets of choice. However this creates a big overhead for applications to support more and more proprietrary SDKs that increase its size and also creates vendor lockin for major wallet providers that can allocate resources to reach more applications. -Users are already installing a wallet applicationg in 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. +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. 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. From 9fe4147512f039e53e40fa37cd9245821daf9eb8 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 30 May 2024 13:35:44 -0500 Subject: [PATCH 06/28] update event names --- CAIPs/caip-282.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 711f1f89..42a37684 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -59,16 +59,16 @@ Here is the expected logic from the blockchain library: const wallets = {}; // blockchain library starts listening on init -window.addEventListener("caipxxx:announceWallet", (event) => { +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: "caipxxx:promptWallet", + event: "caip282:promptWallet", data: { - // if the blockchain library supports CAIP-275 then it can include a name in its prompt + // if the blockchain library supports CAIP-275 then it can include a name name: "", // optional }, }); @@ -88,16 +88,16 @@ const data = { // wallet provider publishes on init window.postMessage("message", { - event: "caipxxx:announceWallet", + event: "caip282:announceWallet", data, }); // wallet providers starts listenning on init -window.addEventListener("caipxxx:promptWallet", (event) => { +window.addEventListener("caip282:promptWallet", (event) => { // when a prompt message was received then the wallet will announces again window.postMessage("message", { - event: "caipxxx:announceWallet", + event: "caip282:announceWallet", data, }); }); @@ -108,7 +108,7 @@ window.addEventListener("caipxxx:promptWallet", (event) => { 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 CAIPXXXWalletData { +interface caip282WalletData { uuid: string; name: string; icon: string; @@ -128,13 +128,13 @@ Here is the expected logic from the blockchain library: ```js // blockchain library listens for responses -window.addEventListener("caipxxx:respond:", (event) => { +window.addEventListener("caip282:respond:", (event) => { console.log(event.data); }); // blockchain library publishes for requests window.postMessage("message", { - event: "caipxxx:request:", + event: "caip282:request:", data: { ... }, }); ``` @@ -143,13 +143,13 @@ Here is the expected logic from the wallet provider: ```js // wallet provider listens for request -window.addEventListener("caipxxx:request:", (event) => { +window.addEventListener("caip282:request:", (event) => { console.log(event.data); }); // wallet provider publishes for reponses window.postMessage("message", { - event: "caipxxx:respond:", + event: "caip282:respond:", data: { ... }, }); ``` From 6ac99d4e4c19316a046f6b35901e70da1462df70 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 13:39:57 +0200 Subject: [PATCH 07/28] Update CAIPs/caip-282.md Co-authored-by: Derek --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 094cdd6a..e8cadb41 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -43,7 +43,7 @@ Blockchain Library: A library or piece of software that assists a DApp to intera ### Messaging APIs -The browser has available 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. +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. This provides the foundation for any wallet provider to interface with a decentralized application using a blockchain library which implements this standard. From 42c645cde930e273133a88125bb2b7c83b6f6a28 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 13:40:07 +0200 Subject: [PATCH 08/28] Update CAIPs/caip-282.md Co-authored-by: Derek --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index e8cadb41..fdd011a9 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -118,7 +118,7 @@ interface caip282WalletData { 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. -#### Connection +#### 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. From 4986b61debfd25f6641307bda837b56e72c73aa9 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 13:40:17 +0200 Subject: [PATCH 09/28] Update CAIPs/caip-282.md Co-authored-by: Derek --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index fdd011a9..f44becbb 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -19,7 +19,7 @@ When interfacing with decentralized applications users install wallets in their ## Motivation -Currently, decentralized applications are required to install a multitude of SDKs to support many different wallet providers to broaden its userbase with different wallets of choice. However this creates a big overhead for applications to support more and more proprietrary SDKs that increase its size and also creates vendor lockin for major wallet providers that can allocate resources to reach more applications. +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. From 0869e0de19a7abd87a32476e9cb9848d2e9ce3da Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 13:43:25 +0200 Subject: [PATCH 10/28] Update CAIPs/caip-282.md Co-authored-by: Derek --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index f44becbb..5e35c5ef 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -15,7 +15,7 @@ Standardized messaging for wallet interface in browser environments. ## Abstract -When interfacing with decentralized applications users install wallets in their browser 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. +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. ## Motivation From f076f22152aaf36c90865db658a751b22e2e2658 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 15:01:56 +0200 Subject: [PATCH 11/28] add missing sections --- CAIPs/caip-282.md | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 5e35c5ef..bccd2350 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -158,9 +158,27 @@ window.postMessage("message", { 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. +#### UUIDs + +The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. + +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. + +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. + +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. + ## Rationale -TODO +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. + +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. + +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. + +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 ## Test Cases @@ -168,15 +186,29 @@ TODO ## Security Considerations -TODO +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: + +### 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. ## Privacy Considerations -TODO +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. ## Backwards Compatibility -TODO +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. + +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. + +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 From eb13968a30d7a03645cf08767784fb6b955e7320 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 15:53:01 +0200 Subject: [PATCH 12/28] add test cases --- CAIPs/caip-282.md | 161 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 3 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index bccd2350..6944312b 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -101,8 +101,6 @@ window.addEventListener("caip282:promptWallet", (event) => { 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: @@ -182,7 +180,164 @@ Finally the use of CAIP-27 leverages the work above to properly target signing r ## Test Cases -TODO +Here is a test case where we demonstrate a scenario with logic from both a blockchain library and a wallet provider + +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, + }); +}); +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 +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 From 1cb66fd883397579ecc73afd19272c06123f0d64 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 15:53:31 +0200 Subject: [PATCH 13/28] tweak --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 6944312b..600aa94a 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -323,7 +323,7 @@ window.postMessage("message", { // 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 +// 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 From 46c13a609e0c9e64cf5dce965eeb571a1cbe56ad Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 6 Jun 2024 17:15:03 +0200 Subject: [PATCH 14/28] Update CAIPs/caip-282.md Co-authored-by: Derek --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 600aa94a..bd53daf0 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -11,7 +11,7 @@ requires: 2, 10, 25, 27, 222, 275 ## Simple Summary -Standardized messaging for wallet interface in browser environments. +Standardized messaging interface for browser wallets. ## Abstract From d2254ba543fe55bc6685835f93f50fda0db3f4f5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 7 Jun 2024 15:40:54 +0200 Subject: [PATCH 15/28] Update CAIPs/caip-282.md Co-authored-by: Gregory Rocco --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index bd53daf0..617f81ae 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -5,7 +5,7 @@ author: Pedro Gomes (@pedrouid) discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/282 status: Draft type: Standard -created: 2023-05-30 +created: 2024-05-30 requires: 2, 10, 25, 27, 222, 275 --- From 9f5029f4fc2a8455bd21549b85a3d2c7bffa24b6 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 8 Jun 2024 12:43:46 +0200 Subject: [PATCH 16/28] Update CAIPs/caip-282.md Co-authored-by: Gregory Rocco --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 617f81ae..1a779ec6 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -11,7 +11,7 @@ requires: 2, 10, 25, 27, 222, 275 ## Simple Summary -Standardized messaging interface for browser wallets. +CAIP-282 defines a standardized messaging interface for browser wallets. ## Abstract From d0562ea8e9db4fba86328e0ad1a51b81ecb59054 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 8 Jun 2024 12:46:14 +0200 Subject: [PATCH 17/28] Apply suggestions from code review Co-authored-by: Gregory Rocco --- CAIPs/caip-282.md | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 1a779ec6..03ea5c32 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -15,19 +15,19 @@ CAIP-282 defines a standardized messaging interface for browser wallets. ## 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. +To interface with decentralized applications (dapps), users install browser wallets to manage their blockchain accounts, which are required to sign messages and transactions. Leveraging existing browser messaging APIs, these are used to initiate a dapp-wallet connection in a browser environment. ## 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. +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 install extra software to support these wallet providers. This situation is only present due to the lack of standardization and interoperability between interfaces and discovery mechanisms for different wallet providers. 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. +This situation is further aggravated by differences between blockchain networks such as Ethereum, Cosmos, Solana, Tezos, etc. While some solutions attempt to solve this, such as WalletConnect, [EIP-6963][eip-6963], Solana Wallet Protocol, etc., they do not cover all wallets and are not chain-angostic. -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. +In this proposal, we present a solution focused on optimizing interoperability for multiple Wallet Providers, fostering fairer competition by reducing the barriers to entry for new Wallet Providers, and enhancing user experience across all blockchain networks. ## Specification @@ -39,7 +39,7 @@ Wallet Provider: A user agent that manages accounts and facilitates transactions 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. -Blockchain Library: A library or piece of software that assists a DApp to interact with blockchain and interface with the Wallet. +Blockchain Library: A library or piece of software that assists a DApp to interact with a blockchain and interface with the Wallet. ### Messaging APIs @@ -47,7 +47,7 @@ The browser exposes two APIs that can be used for messaging across different par 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. +Different loading times can be affected by multiple factors, which makes it non-deterministic to publish and listen to messages from different sources within the browser. #### Discovery @@ -120,7 +120,7 @@ The parameters `name` and `icon` are used to display to the user to be easily re 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. -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. +The communication will use the `uuid` shared by the initial wallet provider announcement payload, which the wallet provider will listen to for any incoming requests, and consequently, the blockchain library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the wallet provider will be the blockchain library that will be listening to any incoming responses, and consequently, the wallet provider will also use it for publishing messages. Here is the expected logic from the blockchain library: @@ -154,33 +154,33 @@ window.postMessage("message", { #### 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. +This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. #### UUIDs The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. -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. +A wallet provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. -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. +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25] or [CAIP-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. -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. +Once established, the UUID is used as `sessionId` for the [CAIP-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. ## 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. +Browser wallets differentiate themselves because they can be installed by users without the application developer requiring 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 uuids and rdns. -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. +The choice for using `window.postMessage` is motivated by expanding the range of wallet providers it can support, including browser extensions that can alternatively use `window.dispatchEvent` but instead it would also cover Inline Frames, Service Workers, Shared Workers, and more. -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. +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, wallet providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. 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 +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 ## Test Cases -Here is a test case where we demonstrate a scenario with logic from both a blockchain library and a wallet provider +Here is a test case where we demonstrate a scenario with logic from both a blockchain library and a wallet provider. Logic from the blockchain library: @@ -341,7 +341,7 @@ window.postMessage("message", { ## 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: +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: ### Wallet Imitation and Manipulation @@ -349,30 +349,32 @@ Application developers are expected to actively detect for misbehavior of proper ### 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. +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 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 from being used as an obfuscation technique to hide malicious modifications to the page or to other wallets. ## 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. +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. Wallet providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where wallet providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that wallet providers offer this more "private connect" feature that users only enable optionally, rather than set by default. ## 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. +For the EIP155 (Ethereum) ecosystem there are already interfaces for the 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. -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. +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. 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 +- [EIP-6963][eip-6963] - Multi Injected Provider Discovery - [CAIP-2][caip-2] - Blockchain ID Specification - [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 +[eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 [caip-2]: https://chainagnostic.org/CAIPs/caip-2 [caip-10]: https://chainagnostic.org/CAIPs/caip-10 [caip-27]: https://chainagnostic.org/CAIPs/caip-27 From 2eb5f0c733d10846b2ad1efd24e0e44edb9e5a40 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 8 Jun 2024 13:26:05 +0200 Subject: [PATCH 18/28] clean up and introduction of CAIP-217 scopes --- CAIPs/caip-282.md | 128 ++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 03ea5c32..f3204b75 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -6,7 +6,7 @@ discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/282 status: Draft type: Standard created: 2024-05-30 -requires: 2, 10, 25, 27, 222, 275 +requires: 25, 27, 217, 222, 275 --- ## Simple Summary @@ -15,15 +15,15 @@ CAIP-282 defines a standardized messaging interface for browser wallets. ## Abstract -To interface with decentralized applications (dapps), users install browser wallets to manage their blockchain accounts, which are required to sign messages and transactions. Leveraging existing browser messaging APIs, these are used to initiate a dapp-wallet connection in a browser environment. +To interface with a Decentralized Application (dapp), users install browser wallets to manage their blockchain accounts, which are required to sign messages and transactions. Leveraging existing browser messaging APIs, these are used to initiate a dapp-wallet connection in a browser environment. ## 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. +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 install extra software to support these wallet providers. This situation is only present due to the lack of standardization and interoperability between interfaces and discovery mechanisms for different wallet providers. +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 install extra software to support these Wallet Providers. This situation is only present due to the lack of standardization and interoperability between interfaces and discovery mechanisms for different Wallet Providers. -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 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 attempt to solve this, such as WalletConnect, [EIP-6963][eip-6963], Solana Wallet Protocol, etc., they do not cover all wallets and are not chain-angostic. @@ -37,47 +37,47 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL Wallet Provider: A user agent that manages accounts and facilitates transactions with a blockchain. -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. +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. -Blockchain Library: A library or piece of software that assists a DApp to interact with a blockchain and interface with the Wallet. +Blockchain Library: A library or piece of software that assists a dapp to interact with a blockchain and interface with the Wallet. ### 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. -This provides the foundation for any wallet provider to interface with a decentralized application using a blockchain library which implements this standard. +This provides the foundation for any Wallet Provider to interface with a Decentralized Application using a Blockchain Library which implements this standard. Different loading times can be affected by multiple factors, which makes it non-deterministic to publish and listen to messages from different sources within the browser. #### 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. +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: +Here is the expected logic from the Blockchain Library: ```js const wallets = {}; -// blockchain library starts listening on init +// 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 +// 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 + // if the Blockchain Library supports CAIP-275 then it can include a name + authName: "", // optional }, }); ``` -Here is the expected logic from the wallet provider: +Here is the expected logic from the Wallet Provider: ```js -// wallet provider sets data on init +// Wallet Provider sets data on init const data = { uuid: ""; name: ""; @@ -86,14 +86,14 @@ const data = { } -// wallet provider publishes on init +// Wallet Provider publishes on init window.postMessage("message", { event: "caip282:announceWallet", data, }); -// wallet providers starts listenning on init +// 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", { @@ -103,49 +103,68 @@ window.addEventListener("caip282:promptWallet", (event) => { }); ``` -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: +#### WalletData + +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 { +interface WalletData { uuid: string; name: string; icon: string; rdns: string; + scps?: AuthorizationScopes; } ``` 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. +The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. + +```typescript +// Defined by CAIP-217 +interface AuthorizationScopes { + [scopeString: string]: { + scopes?: string[]; + methods: string[]; + notifications: string[]; + accounts?: string[]; + rpcDocuments?: string[]; + rpcEndpoints?: string[]; + }; +} +``` + #### 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. +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. -The communication will use the `uuid` shared by the initial wallet provider announcement payload, which the wallet provider will listen to for any incoming requests, and consequently, the blockchain library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the wallet provider will be the blockchain library that will be listening to any incoming responses, and consequently, the wallet provider will also use it for publishing messages. +The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. -Here is the expected logic from the blockchain library: +Here is the expected logic from the Blockchain Library: ```js -// blockchain library listens for responses +// Blockchain Library listens for responses window.addEventListener("caip282:respond:", (event) => { console.log(event.data); }); -// blockchain library publishes for requests +// Blockchain Library publishes for requests window.postMessage("message", { event: "caip282:request:", data: { ... }, }); ``` -Here is the expected logic from the wallet provider: +Here is the expected logic from the Wallet Provider: ```js -// wallet provider listens for request +// Wallet Provider listens for request window.addEventListener("caip282:request:", (event) => { console.log(event.data); }); -// wallet provider publishes for reponses +// Wallet Provider publishes for reponses window.postMessage("message", { event: "caip282:respond:", data: { ... }, @@ -160,19 +179,19 @@ This same channel `uuid` can then be used for a connected session using [CAIP-27 The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. -A wallet provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. +A Wallet Provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. -A UUID can be re-used as a `sessionId` if and only if the [CAIP-25] or [CAIP-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] or [CAIP-222][caip-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. -Once established, the UUID is used as `sessionId` for the [CAIP-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. +Once established, the UUID is used as `sessionId` for the [CAIP-27][caip-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. ## Rationale Browser wallets differentiate themselves because they can be installed by users without the application developer requiring 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 uuids and rdns. -The choice for using `window.postMessage` is motivated by expanding the range of wallet providers it can support, including browser extensions that can alternatively use `window.dispatchEvent` but instead it would also cover Inline Frames, Service Workers, Shared Workers, and more. +The choice for using `window.postMessage` is motivated by expanding the range of Wallet Providers it can support, including browser extensions that can alternatively use `window.dispatchEvent` but instead it would also cover Inline Frames, Service Workers, Shared Workers, and more. -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, wallet providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. +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, Wallet Providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. 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. @@ -180,12 +199,12 @@ Finally the use of CAIP-27 leverages the work above to properly target signing r ## Test Cases -Here is a test case where we demonstrate a scenario with logic from both a blockchain library and a wallet provider. +Here is a test case where we demonstrate a scenario with logic from both a Blockchain Library and a Wallet Provider. -Logic from the blockchain library: +Logic from the Blockchain Library: ```js -// 1. blockchain library initializes by listening to announceWallet messages and +// 1. Blockchain Library initializes by listening to announceWallet messages and // also by posting a prompt message const wallets = {}; window.addEventListener("caip282:announceWallet", (event) => { @@ -196,7 +215,7 @@ 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 +// 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); @@ -223,7 +242,7 @@ window.postMessage("message", { }, }); -// 4. After the response was received by the blockchain library from the wallet +// 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 = {}; @@ -263,11 +282,11 @@ window.postMessage("message", { ``` -Logic from the wallet provider: +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 +// 1. Wallet Provider sets their WalletData and then listens to promptWallet message +// and also immediatelly posts a message with the WalletData as announceWallet type const data = { uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" name: "Example Wallet", @@ -288,14 +307,14 @@ window.postMessage("message", { // 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 +// 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 +// 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 +// Wallet Provider publishes for reponses window.postMessage("message", { event: "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", data: { @@ -321,14 +340,14 @@ window.postMessage("message", { }, }); -// 4. Once the connection is established then the Wallet provider can receive +// 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 +// Wallet Provider publishes for reponses window.postMessage("message", { event: "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", data: { @@ -345,40 +364,39 @@ The advantage of using `window.postMessage` over existing standards that leverag ### 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. +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 WalletData objects match. Applications and Libraries are expected to consider other potential methods that the WalletData 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 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 from being used as an obfuscation technique to hide malicious modifications to the page or to other wallets. +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 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 from being used as an obfuscation technique to hide malicious modifications to the page or to other wallets. ## 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. Wallet providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where wallet providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that wallet providers offer this more "private connect" feature that users only enable optionally, rather than set by default. +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. Wallet Providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. ## 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 the EIP155 (Ethereum) ecosystem there are already interfaces for the 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. +For the EIP155 (Ethereum) ecosystem there are already interfaces for the discoverability of browser wallets through either legacy `window.ethereum` or [EIP-6963][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. 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. -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. +The WalletData 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 - [EIP-6963][eip-6963] - Multi Injected Provider Discovery -- [CAIP-2][caip-2] - Blockchain ID Specification -- [CAIP-10][caip-10] - Account ID Specification - [CAIP-27][caip-27] - Blockchain ID Specification - [CAIP-25][caip-25] - Blockchain ID Specification +- [CAIP-217][caip-217] - Provider Authorization Scopes - [CAIP-222][caip-222] - Account ID Specification +- [CAIP-275][caip-275] - Domain Wallet Authentication [eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 -[caip-2]: https://chainagnostic.org/CAIPs/caip-2 -[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-217]: https://chainagnostic.org/CAIPs/caip-217 [caip-222]: https://chainagnostic.org/CAIPs/caip-222 [caip-275]: https://chainagnostic.org/CAIPs/caip-275 From 19a443c059ae3acdd9dc34f3edaa2d906ad06d5b Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 21 Jun 2024 19:05:10 +0200 Subject: [PATCH 19/28] WIP --- CAIPs/caip-282.md | 165 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 39 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index f3204b75..5a2b4395 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -59,15 +59,21 @@ Here is the expected logic from the Blockchain Library: 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; +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_announce') { + // when an announce message was received then the library can index it by uuid + wallets[event.data.params.uuid] = event.data.params + } }); // Blockchain Library publishes on init -window.postMessage("message", { - event: "caip282:promptWallet", - data: { +window.postMessage({ + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: { + // optionally the Blockchain Library can prompt wallets to announce matching only the chains + chains: [] // optional // if the Blockchain Library supports CAIP-275 then it can include a name authName: "", // optional }, @@ -78,7 +84,7 @@ Here is the expected logic from the Wallet Provider: ```js // Wallet Provider sets data on init -const data = { +const walletData = { uuid: ""; name: ""; icon: ""; @@ -87,19 +93,25 @@ const data = { // Wallet Provider publishes on init -window.postMessage("message", { - event: "caip282:announceWallet", - data, +window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: 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, - }); +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_prompt') { + // when a prompt message was received then the wallet will announces again + window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: data + }); + } }); ``` @@ -144,31 +156,62 @@ The communication will use the `uuid` shared by the initial Wallet Provider anno Here is the expected logic from the Blockchain Library: ```js +// Blockchain Library creates a JSON-RPC request +const request = { + id: 123, + jsonrpc: "2.0", + method: "wallet_request", + params: { + // UUID from WalletData is used as SessionId + sessionId: walletData.uuid, + scope: "chain:777", + request: { + method: "chain_signMessage", + params: [ + "Verifying my wallet with this message", + "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", + ], + }, + }, +}; + // Blockchain Library listens for responses -window.addEventListener("caip282:respond:", (event) => { - console.log(event.data); +window.addEventListener("message", (event) => { + if (event.data.id === request.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + console.log(event.data.result); + } + } }); // Blockchain Library publishes for requests -window.postMessage("message", { - event: "caip282:request:", - data: { ... }, -}); +window.postMessage(request); ``` Here is the expected logic from the Wallet Provider: ```js // Wallet Provider listens for request -window.addEventListener("caip282:request:", (event) => { - console.log(event.data); +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_request") { + // if incoming requests match the WalletData UUID + if (event.data.params.sessionId === walletData.uuid) { + console.log(event.data); + } + } }); +const response = { + id: event.data.id, + jsonrpc: "2.0", + result: { ... } +} + // Wallet Provider publishes for reponses -window.postMessage("message", { - event: "caip282:respond:", - data: { ... }, -}); +window.postMessage("message", response); ``` #### Signing @@ -207,17 +250,61 @@ Logic from the Blockchain Library: // 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.addEventListener("message", (event) => { + if (event.data.method === 'wallet_announce') { + // when an announce message was received then the library can index it by uuid + wallets[event.data.params.uuid] = event.data.params + } +}); + +window.postMessage({ + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: {}, }); -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 + +const request = { + id: 123, + jsonrpc: "2.0", + method: "wallet_createSession", + params: { + optionalScopes: { + eip155: { + scopes: ["chain:777"], + methods: ["chain_signMessage", "chain_sendTransaction"], + notifications: ["accountsChanged"], + }, + }, + sessionProperties: { + expiry: "2024-06-06T13:10:48.155Z", + }, + }, +}; + +let session = {} + +window.addEventListener("message", (event) => { + if (event.data.id === request.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + console.log(event.data.result); + } + } +}); + +window.postMessage(request); + let session = {}; -window.addEventListener("caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", (event) => { +window.addEventListener("message", "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", (event) => { if (event.data.error) throw new Error(event.data.error.message); session = event.data.result; }); @@ -246,7 +333,7 @@ window.postMessage("message", { // 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) => { +window.addEventListener("message", "caip282:respond:350670db-19fa-4704-a166-e52e178b59d2", (event) => { if (event.data.error) throw new Error(event.data.error.message); result = event.data.result; }); @@ -287,13 +374,13 @@ Logic from the Wallet Provider: ```js // 1. Wallet Provider sets their WalletData and then listens to promptWallet message // and also immediatelly posts a message with the WalletData as announceWallet type -const data = { +const walletData = { uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" name: "Example Wallet", - icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - rdns: "com.example.wallet"; + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", + rdns: "com.example.wallet", } -window.addEventListener("caip282:promptWallet", (event) => { +window.addEventListener("message", "caip282:promptWallet", (event) => { // when a prompt message was received then the wallet will announces again window.postMessage("message", { event: "caip282:announceWallet", @@ -311,7 +398,7 @@ window.postMessage("message", { // 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) => { +window.addEventListener("message", "caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => { request = event.data }); // Wallet Provider publishes for reponses @@ -344,7 +431,7 @@ window.postMessage("message", { // 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) => { +window.addEventListener("message", "caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => { request = event.data }); // Wallet Provider publishes for reponses From f2389e8b77f7c9bf593465b6eb2129c9c4c2cfa8 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 24 Jun 2024 23:12:22 +0100 Subject: [PATCH 20/28] move to jsonrpc --- CAIPs/caip-282.md | 289 ++++++++++++++++++++++------------------------ 1 file changed, 137 insertions(+), 152 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 5a2b4395..eab98f3c 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -53,6 +53,45 @@ Different loading times can be affected by multiple factors, which makes it non- 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. +This discovery would use the following JSON-RPC request params: + +```typescript +// for "wallet_prompt" method +interface WalletPromptRequestParams { + chains: string[]; + authName: string; +} + +// for "wallet_announce" method +interface WalletAnnounceRequestParams { + uuid: string; + name: string; + icon: string; + rdns: string; + scopes?: AuthorizationScopes; +} +``` + +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. + +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. + +The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. + +```typescript +// Defined by CAIP-217 +interface AuthorizationScopes { + [scopeString: string]: { + scopes?: string[]; + methods: string[]; + notifications: string[]; + accounts?: string[]; + rpcDocuments?: string[]; + rpcEndpoints?: string[]; + }; +} +``` + Here is the expected logic from the Blockchain Library: ```js @@ -84,20 +123,14 @@ Here is the expected logic from the Wallet Provider: ```js // Wallet Provider sets data on init -const walletData = { - uuid: ""; - name: ""; - icon: ""; - rdns: ""; -} - +const walletData = { ... } // Wallet Provider publishes on init window.postMessage({ id: 2, jsonrpc: "2.0" method: "wallet_announce", - params: data + params: walletData }); @@ -109,50 +142,22 @@ window.addEventListener("message", (event) => { id: 2, jsonrpc: "2.0" method: "wallet_announce", - params: data + params: walletData }); } }); ``` -#### WalletData - -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 WalletData { - uuid: string; - name: string; - icon: string; - rdns: string; - scps?: AuthorizationScopes; -} -``` - -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. - -The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. - -```typescript -// Defined by CAIP-217 -interface AuthorizationScopes { - [scopeString: string]: { - scopes?: string[]; - methods: string[]; - notifications: string[]; - accounts?: string[]; - rpcDocuments?: string[]; - rpcEndpoints?: string[]; - }; -} -``` - #### 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. The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. +#### Signing + +This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. + Here is the expected logic from the Blockchain Library: ```js @@ -214,10 +219,6 @@ const response = { window.postMessage("message", response); ``` -#### Signing - -This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. - #### UUIDs The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. @@ -270,7 +271,7 @@ window.postMessage({ // 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 -const request = { +const sessionRequest = { id: 123, jsonrpc: "2.0", method: "wallet_createSession", @@ -288,85 +289,57 @@ const request = { }, }; -let session = {} +let sessionResult = {} window.addEventListener("message", (event) => { - if (event.data.id === request.id) { + if (event.data.id === sessionRequest.id) { // Get JSON-RPC response if (event.data.error) { console.error(event.data.error.message); } else { - console.log(event.data.result); + sessionResult = event.data.result } } }); -window.postMessage(request); +window.postMessage(sessionRequest); -let session = {}; -window.addEventListener("message", "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("message", "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", - } - ] - } - } - } +const signingRequest = { + id: 456, + jsonrpc: "2.0", + method: "wallet_request", + params: { + // UUID from WalletData is used as SessionId + sessionId: walletData.uuid, + scope: "chain:777", + request: { + method: "chain_signMessage", + params: [ + "Verifying my wallet with this message", + "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", + ], + }, }, +}; + +let signingResult = {} + +window.addEventListener("message", (event) => { + if (event.data.id === signingRequest.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + signingResult = event.data.result + } + } }); +window.postMessage(signingRequest); ``` Logic from the Wallet Provider: @@ -380,16 +353,24 @@ const walletData = { icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", rdns: "com.example.wallet", } -window.addEventListener("message", "caip282:promptWallet", (event) => { - // when a prompt message was received then the wallet will announces again - window.postMessage("message", { - event: "caip282:announceWallet", - data, - }); + +window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData }); -window.postMessage("message", { - event: "caip282:announceWallet", - data, + +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_prompt') { + // when a prompt message was received then the wallet will announces again + window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData + }); + } }); // 2. User presses "Connect Wallet" on the application webpage which will select UUID @@ -397,51 +378,55 @@ window.postMessage("message", { // 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("message", "caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => { - request = event.data +const sessionRequest = {} + +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_createSession") { + sessionRequest = event.data + // if incoming requests match the WalletData UUID + if (checkSupportedScopes(event.data.params)) { + // prompt user to approve session + } + } }); -// 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" - ] - }, + +window.postMessage({ + id: sessionRequest.id, // 123 + jsonrpc: "2.0", + result: { + sessionId: walletData.uuid, // "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", - } + }, + 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("message", "caip282:request:350670db-19fa-4704-a166-e52e178b59d2", (event) => { - request = event.data +const signingRequest = {} + +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_createSession" && event.data.params.sessionId === walletData.uuid) { + signingRequest = event.data.params + } }); -// 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" - }, + +window.postMessage({ + id: signingRequest.id // 456 + jsonrpc: "2.0", + result: "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" }); ``` From b6ced5c44ce90b300628b28c88cc453cdb26a1f6 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 26 Jun 2024 22:19:51 +0100 Subject: [PATCH 21/28] add separate caips for extensions and iframes --- CAIPs/caip-294.md | 0 CAIPs/caip-295.md | 475 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 475 insertions(+) create mode 100644 CAIPs/caip-294.md create mode 100644 CAIPs/caip-295.md diff --git a/CAIPs/caip-294.md b/CAIPs/caip-294.md new file mode 100644 index 00000000..e69de29b diff --git a/CAIPs/caip-295.md b/CAIPs/caip-295.md new file mode 100644 index 00000000..b0a4e803 --- /dev/null +++ b/CAIPs/caip-295.md @@ -0,0 +1,475 @@ +--- +caip: 295 +title: Browser Wallet Messaging for Iframes +author: Pedro Gomes (@pedrouid) +discussions-to: https://github.com/ChainAgnostic/CAIPs/issues/295 +status: Draft +type: Standard +created: 2024-05-30 +requires: 25, 27, 282 +--- + +## Simple Summary + +CAIP-295 defines a standardized messaging transport for browser iframe wallets. + +## Abstract + +To interface with a Decentralized Application (dapp), users install browser wallets to manage their blockchain accounts, which are required to sign messages and transactions. Leveraging existing browser messaging APIs, these are used to initiate a dapp-wallet connection in a browser environment. + +## Motivation + +Currently, in order for Decentralized Applications to be able to support all users they need support different messaging standards for each namespace such as Ethereum's [EIP-6963][eip-6963], Solana Wallet Protocol, etc., they do not cover all wallets and are not chain-angostic. + +Developers must support different SDKs for different blockchain namespaces which work very differently despite following the same patterns of discovery, handshake and signing. + +This proposal is motivated by the fragmentation on the events across different ecosystems and aims to bring cohesion to reduce the unnecessary logic to support multiple chains in different namespaces. + +Additionally this aligns the messaging for browser wallets to leverage existing standards for handshake (CAIP-25) and signing (CAIP-27). Thus introducing a solution focused on optimizing interoperability for multiple Wallet Providers, fostering fairer competition by reducing the barriers to entry for new Wallet Providers, and enhancing user experience across all blockchain networks. + +## 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. + +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. + +Blockchain Library: A library or piece of software that assists a dapp to interact with a blockchain and interface with the Wallet. + +### Messaging APIs + +Browser Extensions make use of events with `window.dispatchEvent` and `window.addEventListener` which will be necessary for publishing and listening messages, respectively, between the Blockchain Library used by the Decentralized Application to communicate with the Wallet Provider. + +This provides the foundation for any Wallet Provider to interface with a Decentralized Application using a Blockchain Library which implements this standard. + +Different loading times can be affected by multiple factors, which makes it non-deterministic to publish and listen to messages from different sources within the browser. + +#### 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. + +This discovery would use the following JSON-RPC request params: + +```typescript +// for "wallet_prompt" method +interface WalletPromptRequestParams { + chains: string[]; + authName: string; +} + +// for "wallet_announce" method +interface WalletAnnounceRequestParams { + uuid: string; + name: string; + icon: string; + rdns: string; + scopes?: AuthorizationScopes; +} +``` + +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. + +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. + +The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. + +```typescript +// Defined by CAIP-217 +interface AuthorizationScopes { + [scopeString: string]: { + scopes?: string[]; + methods: string[]; + notifications: string[]; + accounts?: string[]; + rpcDocuments?: string[]; + rpcEndpoints?: string[]; + }; +} +``` + +Here is the expected logic from the Blockchain Library: + +```js +const wallets = {}; + +// Blockchain Library starts listening on init +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_announce') { + // when an announce message was received then the library can index it by uuid + wallets[event.data.params.uuid] = event.data.params + } +}); + +// Blockchain Library publishes on init +window.postMessage({ + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: { + // optionally the Blockchain Library can prompt wallets to announce matching only the chains + chains: [] // optional + // if the Blockchain Library supports CAIP-275 then it can include a name + authName: "", // optional + }, +}); +``` + +Here is the expected logic from the Wallet Provider: + +```js +// Wallet Provider sets data on init +const walletData = { ... } + +// Wallet Provider publishes on init +window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData +}); + + +// Wallet Providers starts listenning on init +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_prompt') { + // when a prompt message was received then the wallet will announces again + window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData + }); + } +}); +``` + +#### 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. + +The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. + +#### Signing + +This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. + +Here is the expected logic from the Blockchain Library: + +```js +// Blockchain Library creates a JSON-RPC request +const request = { + id: 123, + jsonrpc: "2.0", + method: "wallet_request", + params: { + // UUID from WalletData is used as SessionId + sessionId: walletData.uuid, + scope: "chain:777", + request: { + method: "chain_signMessage", + params: [ + "Verifying my wallet with this message", + "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", + ], + }, + }, +}; + +// Blockchain Library listens for responses +window.addEventListener("message", (event) => { + if (event.data.id === request.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + console.log(event.data.result); + } + } +}); + +// Blockchain Library publishes for requests +window.postMessage(request); +``` + +Here is the expected logic from the Wallet Provider: + +```js +// Wallet Provider listens for request +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_request") { + // if incoming requests match the WalletData UUID + if (event.data.params.sessionId === walletData.uuid) { + console.log(event.data); + } + } +}); + +const response = { + id: event.data.id, + jsonrpc: "2.0", + result: { ... } +} + +// Wallet Provider publishes for reponses +window.postMessage("message", response); +``` + +#### UUIDs + +The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. + +A Wallet Provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. + +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] or [CAIP-222][caip-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. + +Once established, the UUID is used as `sessionId` for the [CAIP-27][caip-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. + +## Rationale + +Browser wallets differentiate themselves because they can be installed by users without the application developer requiring 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 uuids and rdns. + +The choice for using `window.postMessage` is motivated by expanding the range of Wallet Providers it can support, including browser extensions that can alternatively use `window.dispatchEvent` but instead it would also cover Inline Frames, Service Workers, Shared Workers, and more. + +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, Wallet Providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. + +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 + +## Test Cases + +Here is a test case where we demonstrate a scenario with logic from both a Blockchain Library and a Wallet Provider. + +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("message", (event) => { + if (event.data.method === 'wallet_announce') { + // when an announce message was received then the library can index it by uuid + wallets[event.data.params.uuid] = event.data.params + } +}); + +window.postMessage({ + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: {}, +}); + +// 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 + +const sessionRequest = { + id: 123, + jsonrpc: "2.0", + method: "wallet_createSession", + params: { + optionalScopes: { + eip155: { + scopes: ["chain:777"], + methods: ["chain_signMessage", "chain_sendTransaction"], + notifications: ["accountsChanged"], + }, + }, + sessionProperties: { + expiry: "2024-06-06T13:10:48.155Z", + }, + }, +}; + +let sessionResult = {} + +window.addEventListener("message", (event) => { + if (event.data.id === sessionRequest.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + sessionResult = event.data.result + } + } +}); + +window.postMessage(sessionRequest); + + +// 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 +const signingRequest = { + id: 456, + jsonrpc: "2.0", + method: "wallet_request", + params: { + // UUID from WalletData is used as SessionId + sessionId: walletData.uuid, + scope: "chain:777", + request: { + method: "chain_signMessage", + params: [ + "Verifying my wallet with this message", + "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", + ], + }, + }, +}; + +let signingResult = {} + +window.addEventListener("message", (event) => { + if (event.data.id === signingRequest.id) { + // Get JSON-RPC response + if (event.data.error) { + console.error(event.data.error.message); + } else { + signingResult = event.data.result + } + } +}); + +window.postMessage(signingRequest); +``` + +Logic from the Wallet Provider: + +```js +// 1. Wallet Provider sets their WalletData and then listens to promptWallet message +// and also immediatelly posts a message with the WalletData as announceWallet type +const walletData = { + uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" + name: "Example Wallet", + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", + rdns: "com.example.wallet", +} + +window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData +}); + +window.addEventListener("message", (event) => { + if (event.data.method === 'wallet_prompt') { + // when a prompt message was received then the wallet will announces again + window.postMessage({ + id: 2, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData + }); + } +}); + +// 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 sessionRequest = {} + +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_createSession") { + sessionRequest = event.data + // if incoming requests match the WalletData UUID + if (checkSupportedScopes(event.data.params)) { + // prompt user to approve session + } + } +}); + +window.postMessage({ + id: sessionRequest.id, // 123 + jsonrpc: "2.0", + result: { + sessionId: walletData.uuid, // "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 signingRequest = {} + +window.addEventListener("message", (event) => { + if (event.data.method === "wallet_createSession" && event.data.params.sessionId === walletData.uuid) { + signingRequest = event.data.params + } +}); + +window.postMessage({ + id: signingRequest.id // 456 + 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: + +### 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 WalletData objects match. Applications and Libraries are expected to consider other potential methods that the WalletData 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 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 from being used as an obfuscation technique to hide malicious modifications to the page or to other wallets. + +## 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. Wallet Providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. + +## 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 the EIP155 (Ethereum) ecosystem there are already interfaces for the discoverability of browser wallets through either legacy `window.ethereum` or [EIP-6963][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. + +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. + +The WalletData 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 + +- [EIP-6963][eip-6963] - Multi Injected Provider Discovery +- [CAIP-27][caip-27] - Blockchain ID Specification +- [CAIP-25][caip-25] - Blockchain ID Specification +- [CAIP-217][caip-217] - Provider Authorization Scopes +- [CAIP-222][caip-222] - Account ID Specification +- [CAIP-275][caip-275] - Domain Wallet Authentication + +[eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 +[caip-27]: https://chainagnostic.org/CAIPs/caip-27 +[caip-25]: https://chainagnostic.org/CAIPs/caip-25 +[caip-217]: https://chainagnostic.org/CAIPs/caip-217 +[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). From 630a3e10f3de6191eec56a4ddfb491495c29fdae Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 26 Jun 2024 23:25:53 +0100 Subject: [PATCH 22/28] dispatchEvent and postMessage --- CAIPs/caip-282.md | 352 ++++--------------------------------- CAIPs/caip-294.md | 431 ++++++++++++++++++++++++++++++++++++++++++++++ CAIPs/caip-295.md | 205 +++++++--------------- 3 files changed, 519 insertions(+), 469 deletions(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index eab98f3c..303599ac 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -1,17 +1,17 @@ --- caip: 282 -title: Browser Wallet Messaging Interface +title: Browser Wallet Discovery Interface author: Pedro Gomes (@pedrouid) discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/282 status: Draft type: Standard created: 2024-05-30 -requires: 25, 27, 217, 222, 275 +requires: 25, 27, 217, 275 --- ## Simple Summary -CAIP-282 defines a standardized messaging interface for browser wallets. +CAIP-282 defines a standardized interface for browser wallet discovery ## Abstract @@ -41,14 +41,6 @@ Decentralized Application (dapp): A web page that relies upon one or many Web3 p Blockchain Library: A library or piece of software that assists a dapp to interact with a blockchain and interface with the Wallet. -### 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. - -This provides the foundation for any Wallet Provider to interface with a Decentralized Application using a Blockchain Library which implements this standard. - -Different loading times can be affected by multiple factors, which makes it non-deterministic to publish and listen to messages from different sources within the browser. - #### 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. @@ -58,8 +50,8 @@ This discovery would use the following JSON-RPC request params: ```typescript // for "wallet_prompt" method interface WalletPromptRequestParams { - chains: string[]; - authName: string; + chains?: string[]; // compatible with CAIP-2 + authName?: string; // compatible with CAIP-275 } // for "wallet_announce" method @@ -92,140 +84,13 @@ interface AuthorizationScopes { } ``` -Here is the expected logic from the Blockchain Library: - -```js -const wallets = {}; - -// Blockchain Library starts listening on init -window.addEventListener("message", (event) => { - if (event.data.method === 'wallet_announce') { - // when an announce message was received then the library can index it by uuid - wallets[event.data.params.uuid] = event.data.params - } -}); - -// Blockchain Library publishes on init -window.postMessage({ - id: 1, - jsonrpc: "2.0" - method: "wallet_prompt", - params: { - // optionally the Blockchain Library can prompt wallets to announce matching only the chains - chains: [] // optional - // if the Blockchain Library supports CAIP-275 then it can include a name - authName: "", // optional - }, -}); -``` - -Here is the expected logic from the Wallet Provider: - -```js -// Wallet Provider sets data on init -const walletData = { ... } - -// Wallet Provider publishes on init -window.postMessage({ - id: 2, - jsonrpc: "2.0" - method: "wallet_announce", - params: walletData -}); - - -// Wallet Providers starts listenning on init -window.addEventListener("message", (event) => { - if (event.data.method === 'wallet_prompt') { - // when a prompt message was received then the wallet will announces again - window.postMessage({ - id: 2, - jsonrpc: "2.0" - method: "wallet_announce", - params: walletData - }); - } -}); -``` - -#### 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. - -The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. - -#### Signing - -This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. - -Here is the expected logic from the Blockchain Library: - -```js -// Blockchain Library creates a JSON-RPC request -const request = { - id: 123, - jsonrpc: "2.0", - method: "wallet_request", - params: { - // UUID from WalletData is used as SessionId - sessionId: walletData.uuid, - scope: "chain:777", - request: { - method: "chain_signMessage", - params: [ - "Verifying my wallet with this message", - "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", - ], - }, - }, -}; - -// Blockchain Library listens for responses -window.addEventListener("message", (event) => { - if (event.data.id === request.id) { - // Get JSON-RPC response - if (event.data.error) { - console.error(event.data.error.message); - } else { - console.log(event.data.result); - } - } -}); - -// Blockchain Library publishes for requests -window.postMessage(request); -``` - -Here is the expected logic from the Wallet Provider: - -```js -// Wallet Provider listens for request -window.addEventListener("message", (event) => { - if (event.data.method === "wallet_request") { - // if incoming requests match the WalletData UUID - if (event.data.params.sessionId === walletData.uuid) { - console.log(event.data); - } - } -}); - -const response = { - id: event.data.id, - jsonrpc: "2.0", - result: { ... } -} - -// Wallet Provider publishes for reponses -window.postMessage("message", response); -``` - #### UUIDs The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. A Wallet Provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. -A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] or [CAIP-222][caip-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. Once established, the UUID is used as `sessionId` for the [CAIP-27][caip-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. @@ -237,202 +102,45 @@ The choice for using `window.postMessage` is motivated by expanding the range of 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, Wallet Providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. -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. +The existing standards around wallet session creation (CAIP-25) 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 +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. ## Test Cases -Here is a test case where we demonstrate a scenario with logic from both a Blockchain Library and a Wallet Provider. - -Logic from the Blockchain Library: - -```js -// 1. Blockchain Library initializes by listening to announceWallet messages and -// also by posting a prompt message -const wallets = {}; +Here are some illustrative examples for both JSON-RPC request params: -window.addEventListener("message", (event) => { - if (event.data.method === 'wallet_announce') { - // when an announce message was received then the library can index it by uuid - wallets[event.data.params.uuid] = event.data.params - } -}); - -window.postMessage({ +```typescript +// Example for wallet_prompt +{ id: 1, jsonrpc: "2.0" method: "wallet_prompt", - params: {}, -}); - -// 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 - -const sessionRequest = { - id: 123, - jsonrpc: "2.0", - method: "wallet_createSession", - params: { - optionalScopes: { - eip155: { - scopes: ["chain:777"], - methods: ["chain_signMessage", "chain_sendTransaction"], - notifications: ["accountsChanged"], - }, - }, - sessionProperties: { - expiry: "2024-06-06T13:10:48.155Z", - }, - }, -}; - -let sessionResult = {} - -window.addEventListener("message", (event) => { - if (event.data.id === sessionRequest.id) { - // Get JSON-RPC response - if (event.data.error) { - console.error(event.data.error.message); - } else { - sessionResult = event.data.result - } - } -}); - -window.postMessage(sessionRequest); - - -// 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 -const signingRequest = { - id: 456, - jsonrpc: "2.0", - method: "wallet_request", params: { - // UUID from WalletData is used as SessionId - sessionId: walletData.uuid, - scope: "chain:777", - request: { - method: "chain_signMessage", - params: [ - "Verifying my wallet with this message", - "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", - ], - }, - }, -}; - -let signingResult = {} - -window.addEventListener("message", (event) => { - if (event.data.id === signingRequest.id) { - // Get JSON-RPC response - if (event.data.error) { - console.error(event.data.error.message); - } else { - signingResult = event.data.result - } + chains: ["chain:777", "chain:888", "chain:999"] // optional + // if the Blockchain Library supports CAIP-275 then it can include a name + authName: "johndoe.chain", // optional } -}); - -window.postMessage(signingRequest); -``` - -Logic from the Wallet Provider: - -```js -// 1. Wallet Provider sets their WalletData and then listens to promptWallet message -// and also immediatelly posts a message with the WalletData as announceWallet type -const walletData = { - uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" - name: "Example Wallet", - icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", - rdns: "com.example.wallet", } -window.postMessage({ + +// Example for wallet_prompt +{ id: 2, jsonrpc: "2.0" method: "wallet_announce", - params: walletData -}); - -window.addEventListener("message", (event) => { - if (event.data.method === 'wallet_prompt') { - // when a prompt message was received then the wallet will announces again - window.postMessage({ - id: 2, - jsonrpc: "2.0" - method: "wallet_announce", - params: walletData - }); - } -}); - -// 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 sessionRequest = {} - -window.addEventListener("message", (event) => { - if (event.data.method === "wallet_createSession") { - sessionRequest = event.data - // if incoming requests match the WalletData UUID - if (checkSupportedScopes(event.data.params)) { - // prompt user to approve session - } - } -}); - -window.postMessage({ - id: sessionRequest.id, // 123 - jsonrpc: "2.0", - result: { - sessionId: walletData.uuid, // "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 signingRequest = {} - -window.addEventListener("message", (event) => { - if (event.data.method === "wallet_createSession" && event.data.params.sessionId === walletData.uuid) { - signingRequest = event.data.params + params: { + uuid: "350670db-19fa-4704-a166-e52e178b59d2", + name: "Example Wallet", + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", + rdns: "com.example.wallet", } -}); - -window.postMessage({ - id: signingRequest.id // 456 - 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: +Regarding security considerations it's important to consider that the WalletData can be imitated and/or manipulated by cross-site scripting and additionally we must consider the following: ### Wallet Imitation and Manipulation @@ -444,17 +152,17 @@ The use of SVG images introduces a cross-site scripting risk as they can include ## 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. Wallet Providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. +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. Wallet Providers can abstain from publishing "Announce" messages on every page load and wait for incoming "Prompt" messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the "Prompt" message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. ## 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 the EIP155 (Ethereum) ecosystem there are already interfaces for the discoverability of browser wallets through either legacy `window.ethereum` or [EIP-6963][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. +For the EIP155 (Ethereum) ecosystem there are already interfaces for the discoverability of browser wallets through either legacy `window.ethereum` or [EIP-6963][eip-6963] events. These existing mechanisms should be supported in parallel without conflict with this new discovery interface. -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. +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 discovery interface. -The WalletData 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. +The WalletData exposed in this messaging interface is also compatible with EIP-6963 and wallet-standard interfaces therefore Wallet Providers can re-use the same identifiers and assets already being used in these existing integrations. ## Links @@ -462,14 +170,12 @@ The WalletData exposed in this messaging interface is also compatible with EIP-6 - [CAIP-27][caip-27] - Blockchain ID Specification - [CAIP-25][caip-25] - Blockchain ID Specification - [CAIP-217][caip-217] - Provider Authorization Scopes -- [CAIP-222][caip-222] - Account ID Specification - [CAIP-275][caip-275] - Domain Wallet Authentication [eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 [caip-27]: https://chainagnostic.org/CAIPs/caip-27 [caip-25]: https://chainagnostic.org/CAIPs/caip-25 [caip-217]: https://chainagnostic.org/CAIPs/caip-217 -[caip-222]: https://chainagnostic.org/CAIPs/caip-222 [caip-275]: https://chainagnostic.org/CAIPs/caip-275 ## Copyright diff --git a/CAIPs/caip-294.md b/CAIPs/caip-294.md index e69de29b..b672c2f3 100644 --- a/CAIPs/caip-294.md +++ b/CAIPs/caip-294.md @@ -0,0 +1,431 @@ +--- +caip: 294 +title: Browser Wallet Messaging for Extensions +author: Pedro Gomes (@pedrouid) +discussions-to: https://github.com/ChainAgnostic/CAIPs/issues/294 +status: Draft +type: Standard +created: 2024-05-30 +requires: 25, 27, 282 +--- + +## Simple Summary + +CAIP-294 defines a standardized messaging transport for browser extension wallets. + +## Abstract + +To interface with a Decentralized Application (dapp), users install browser wallets to manage their blockchain accounts, which are required to sign messages and transactions. Leveraging existing browser messaging APIs, these are used to initiate a dapp-wallet connection in a browser environment. + +## Motivation + +Currently, in order for Decentralized Applications to be able to support all users they need support different messaging standards for each namespace such as Ethereum's [EIP-6963][eip-6963], Solana Wallet Protocol, etc., they do not cover all wallets and are not chain-angostic. + +Developers must support different SDKs for different blockchain namespaces which work very differently despite following the same patterns of discovery, handshake and signing. + +This proposal is motivated by the fragmentation on the events across different ecosystems and aims to bring cohesion to reduce the unnecessary logic to support multiple chains in different namespaces. + +Additionally this aligns the messaging for browser wallets to leverage existing standards for handshake (CAIP-25) and signing (CAIP-27). Thus introducing a solution focused on optimizing interoperability for multiple Wallet Providers, fostering fairer competition by reducing the barriers to entry for new Wallet Providers, and enhancing user experience across all blockchain networks. + +## 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. + +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. + +Blockchain Library: A library or piece of software that assists a dapp to interact with a blockchain and interface with the Wallet. + +### Messaging APIs + +Browser Extensions make use of events with `window.dispatchEvent` and `window.addEventListener` which will be necessary for publishing and listening messages, respectively, between the Blockchain Library used by the Decentralized Application to communicate with the Wallet Provider. + +This provides the foundation for any Wallet Provider to interface with a Decentralized Application using a Blockchain Library which implements this standard. + +Different loading times can be affected by multiple factors, which makes it non-deterministic to publish and listen to messages from different sources within the browser. + +#### 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: + +```typescript +interface WalletMapEntry { + params: WalletAnnounceRequestParams; + eventName: string; +} + +const wallets: Record = {} + +// Blockchain Library starts listening on init +window.addEventListener("caip294:wallet_announce", (event) => { + // when an announce message was received then the library can index it by uuid + wallets[event.detail.params.uuid] = { + params: event.detail.params, + eventName: event.detail.params.uuid + } +}); + +// Blockchain Library publishes on init +window.dispatchEvent(new CustomEvent( + "caip294:wallet_prompt", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: { + // optionally the Blockchain Library can prompt wallets to announce matching only the chains + chains: [] // optional + // if the Blockchain Library supports CAIP-275 then it can include a name + authName: "", // optional + }, + } + } +)); +``` + +Here is the expected logic from the Wallet Provider: + +```typescript +// Wallet Provider sets data on init +const walletData = { ... } + +// Wallet Provider publishes on init +window.dispatchEvent(new CustomEvent( + "caip294:wallet_announce", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData, + } + } +)); + + + +// Wallet Providers starts listenning on init +window.addEventListener("caip294:wallet_prompt", (event) => { + // when a prompt message was received then the wallet will announces again + window.dispatchEvent(new CustomEvent( + "caip294:wallet_announce", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData, + } + } + )); +}); +``` + +#### 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. + +The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. + +#### Signing + +This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. + +#### UUIDs + +The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. + +A Wallet Provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. + +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. + +Once established, the UUID is used as `sessionId` for the [CAIP-27][caip-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. + +## Rationale + +Browser wallets differentiate themselves because they can be installed by users without the application developer requiring 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 uuids and rdns. + +The choice for using `window.postMessage` is motivated by expanding the range of Wallet Providers it can support, including browser extensions that can alternatively use `window.dispatchEvent` but instead it would also cover Inline Frames, Service Workers, Shared Workers, and more. + +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, Wallet Providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. + +The existing standards around wallet session creation (CAIP-25) 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. + +## Test Cases + +Here is a test case where we demonstrate a scenario with logic from both a Blockchain Library and a Wallet Provider. + +Logic from the Blockchain Library: + +```typescript +// 1. Blockchain Library initializes by listening to wallet_announce messages and +// also by posting a prompt message + +interface WalletMapEntry { + params: WalletAnnounceRequestParams; + eventName: string; +} + +const wallets: Record = {} + +window.addEventListener("caip294:wallet_announce", (event) => { + // when an announce message was received then the library can index it by uuid + wallets[event.detail.params.uuid] = { + params: event.detail.params, + eventName: event.detail.params.uuid + } +}); + +window.dispatchEvent(new CustomEvent( + "caip294:wallet_prompt", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_prompt", + params: { + // optionally the Blockchain Library can prompt wallets to announce matching only the chains + chains: [] // optional + // if the Blockchain Library supports CAIP-275 then it can include a name + authName: "", // optional + }, + } + } +)); + +// 2. User presses "Connect Wallet" and the library display the discovered wallets + +const selected_uuid = "350670db-19fa-4704-a166-e52e178b59d2" + +// 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 + +const sessionRequest = { + id: 123, + jsonrpc: "2.0", + method: "wallet_createSession", + params: { + optionalScopes: { + eip155: { + scopes: ["chain:777"], + methods: ["chain_signMessage", "chain_sendTransaction"], + notifications: ["accountsChanged"], + }, + }, + sessionProperties: { + expiry: "2024-06-06T13:10:48.155Z", + }, + }, +}; + +let sessionResult = {} + +window.addEventListener(`caip294:${selected_uuid}`, (event) => { + if (event.detail.id === sessionRequest.id) { + // Get JSON-RPC response + if (event.detail.error) { + console.error(event.detail.error.message); + } else { + sessionResult = event.detail.result + } + } +}); + +window.dispatchEvent(new CustomEvent( + `caip294:${selected_uuid}`, + { + detail: sessionRequest + } +)); + + + +// 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 +const signingRequest = { + id: 456, + jsonrpc: "2.0", + method: "wallet_requestMethod", + params: { + // UUID from WalletData is used as SessionId + sessionId: walletData.uuid, + scope: "chain:777", + request: { + method: "chain_signMessage", + params: [ + "Verifying my wallet with this message", + "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", + ], + }, + }, +}; + +let signingResult = {} + +window.addEventListener(`caip294:${selected_uuid}`, (event) => { + if (event.detail.id === signingRequest.id) { + // Get JSON-RPC response + if (event.detail.error) { + console.error(event.detail.error.message); + } else { + signingResult = event.detail.result + } + } +}); + +window.dispatchEvent(new CustomEvent( + `caip294:${selected_uuid}`, + { + detail: signingRequest + } +)); +``` + +Logic from the Wallet Provider: + +```typescript +// 1. Wallet Provider sets their WalletData and then listens to wallet_prompt message +// and also immediatelly posts a message with the WalletData as wallet_announce type +const walletData = { + uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" + name: "Example Wallet", + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", + rdns: "com.example.wallet", +} + +window.dispatchEvent(new CustomEvent( + "caip294:wallet_announce", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData, + } + } +)); + +window.addEventListener("caip294:wallet_prompt", (event) => { + // when a prompt message was received then the wallet will announces again + window.dispatchEvent(new CustomEvent( + "caip294:wallet_announce", + { + detail: { + id: 1, + jsonrpc: "2.0" + method: "wallet_announce", + params: walletData, + } + } + )); +}); + +// 2. User presses "Connect Wallet" on the application webpage which will select UUID + +const selected_uuid = "350670db-19fa-4704-a166-e52e178b59d2" + +// 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 +let sessionRequest = {} + +window.addEventListener(`caip294:${selected_uuid}`, (event) => { + if (event.detail.method === "wallet_createSession") { + sessionRequest = event.detail + // if incoming requests match the WalletData UUID + if (checkSupportedScopes(event.detail.params)) { + // prompt user to approve session + } + } +}); + +const sessionResponse = { + id: sessionRequest.id, // 123 + jsonrpc: "2.0", + result: { + sessionId: walletData.uuid, // "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", + } + } +} + +window.dispatchEvent(new CustomEvent( + `caip294:${selected_uuid}`, + { + detail: sessionResponse + } +)); + +// 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 +let signingRequest = {} + +window.addEventListener("message", (event) => { + if (event.detail.method === "wallet_createSession" && event.detail.params.sessionId === walletData.uuid) { + signingRequest = event.detail.params + } +}); + +const signingResponse = { + id: signingRequest.id // 456 + jsonrpc: "2.0", + result: "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" +} + +window.dispatchEvent(new CustomEvent( + `caip294:${selected_uuid}`, + { + detail: signingResponse + } +)); +``` + +## Security Considerations + +TODO + +## Privacy Considerations + +TODO + +## Backwards Compatibility + +TODO + +## Links + +- [EIP-6963][eip-6963] - Multi Injected Provider Discovery +- [CAIP-27][caip-27] - Blockchain ID Specification +- [CAIP-25][caip-25] - Blockchain ID Specification +- [CAIP-282][caip-282] - Browser Wallet Discovery Interface + +[eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 +[caip-27]: https://chainagnostic.org/CAIPs/caip-27 +[caip-25]: https://chainagnostic.org/CAIPs/caip-25 +[caip-282]: https://chainagnostic.org/CAIPs/caip-282 + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE). diff --git a/CAIPs/caip-295.md b/CAIPs/caip-295.md index b0a4e803..79786ede 100644 --- a/CAIPs/caip-295.md +++ b/CAIPs/caip-295.md @@ -41,7 +41,7 @@ Blockchain Library: A library or piece of software that assists a dapp to intera ### Messaging APIs -Browser Extensions make use of events with `window.dispatchEvent` and `window.addEventListener` which will be necessary for publishing and listening messages, respectively, between the Blockchain Library used by the Decentralized Application to communicate with the Wallet Provider. +Browser Extensions make use of events with `window.postMessage` and `window.addEventListener` which will be necessary for publishing and listening messages, respectively, between the Blockchain Library used by the Decentralized Application to communicate with the Wallet Provider. This provides the foundation for any Wallet Provider to interface with a Decentralized Application using a Blockchain Library which implements this standard. @@ -51,55 +51,24 @@ Different loading times can be affected by multiple factors, which makes it non- 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. -This discovery would use the following JSON-RPC request params: +Here is the expected logic from the Blockchain Library: ```typescript -// for "wallet_prompt" method -interface WalletPromptRequestParams { - chains: string[]; - authName: string; -} - -// for "wallet_announce" method -interface WalletAnnounceRequestParams { - uuid: string; - name: string; - icon: string; - rdns: string; - scopes?: AuthorizationScopes; +interface WalletMapEntry { + data: WalletAnnounceRequestParams; + targetOrigin: string; } -``` - -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. - -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. -The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. - -```typescript -// Defined by CAIP-217 -interface AuthorizationScopes { - [scopeString: string]: { - scopes?: string[]; - methods: string[]; - notifications: string[]; - accounts?: string[]; - rpcDocuments?: string[]; - rpcEndpoints?: string[]; - }; -} -``` - -Here is the expected logic from the Blockchain Library: - -```js -const wallets = {}; +const wallets: Record = {} // Blockchain Library starts listening on init window.addEventListener("message", (event) => { if (event.data.method === 'wallet_announce') { // when an announce message was received then the library can index it by uuid - wallets[event.data.params.uuid] = event.data.params + wallets[event.data.params.uuid] = { + params: event.data.params, + targetOrigin: event.targetOrigin + } } }); @@ -119,7 +88,7 @@ window.postMessage({ Here is the expected logic from the Wallet Provider: -```js +```typescript // Wallet Provider sets data on init const walletData = { ... } @@ -148,7 +117,7 @@ window.addEventListener("message", (event) => { #### 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. +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. The communication will use the `uuid` shared by the initial Wallet Provider announcement payload, which the Wallet Provider will listen to for any incoming requests, and consequently, the Blockchain Library will also be used for publishing messages. The same will happen again the other way around but vice-versa, where the Wallet Provider will be the Blockchain Library that will be listening to any incoming responses, and consequently, the Wallet Provider will also use it for publishing messages. @@ -156,74 +125,13 @@ The communication will use the `uuid` shared by the initial Wallet Provider anno This same channel `uuid` can then be used for a connected session using [CAIP-27][caip-27] which then would use the `sessionId` from the established connection to identify incoming payloads that need to be respond to, and also which `chainId` is being targetted. -Here is the expected logic from the Blockchain Library: - -```js -// Blockchain Library creates a JSON-RPC request -const request = { - id: 123, - jsonrpc: "2.0", - method: "wallet_request", - params: { - // UUID from WalletData is used as SessionId - sessionId: walletData.uuid, - scope: "chain:777", - request: { - method: "chain_signMessage", - params: [ - "Verifying my wallet with this message", - "0xa89Df33a6f26c29ea23A9Ff582E865C03132b140", - ], - }, - }, -}; - -// Blockchain Library listens for responses -window.addEventListener("message", (event) => { - if (event.data.id === request.id) { - // Get JSON-RPC response - if (event.data.error) { - console.error(event.data.error.message); - } else { - console.log(event.data.result); - } - } -}); - -// Blockchain Library publishes for requests -window.postMessage(request); -``` - -Here is the expected logic from the Wallet Provider: - -```js -// Wallet Provider listens for request -window.addEventListener("message", (event) => { - if (event.data.method === "wallet_request") { - // if incoming requests match the WalletData UUID - if (event.data.params.sessionId === walletData.uuid) { - console.log(event.data); - } - } -}); - -const response = { - id: event.data.id, - jsonrpc: "2.0", - result: { ... } -} - -// Wallet Provider publishes for reponses -window.postMessage("message", response); -``` - #### UUIDs The generation of UUIDs is crucial for this messaging interface to work seamlessly for the users. A Wallet Provider MUST always generate UUIDs distinctly for each web page loaded, and they must not be re-used without a session being established between the application and the wallet with the user's consent. -A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] or [CAIP-222][caip-222] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. +A UUID can be re-used as a `sessionId` if and only if the [CAIP-25][caip-25] procedure has been prompted to the user and the user has approved its permissions to allow the application to make future signing requests. Once established, the UUID is used as `sessionId` for the [CAIP-27][caip-27] payloads, which can verify that incoming messages are being routed through the appropriate channels. @@ -235,9 +143,9 @@ The choice for using `window.postMessage` is motivated by expanding the range of 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, Wallet Providers can track these sessions more granularly rather than making assumptions around the webpage URL and RDNS relationship. -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. +The existing standards around wallet session creation (CAIP-25) 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 +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. ## Test Cases @@ -245,15 +153,23 @@ Here is a test case where we demonstrate a scenario with logic from both a Block Logic from the Blockchain Library: -```js -// 1. Blockchain Library initializes by listening to announceWallet messages and +```typescript +// 1. Blockchain Library initializes by listening to wallet_announce messages and // also by posting a prompt message -const wallets = {}; +interface WalletMapEntry { + data: WalletAnnounceRequestParams; + targetOrigin: string; +} + +const wallets: Record = {} window.addEventListener("message", (event) => { if (event.data.method === 'wallet_announce') { // when an announce message was received then the library can index it by uuid - wallets[event.data.params.uuid] = event.data.params + wallets[event.data.params.uuid] = { + params: event.data.params, + targetOrigin: event.targetOrigin + } } }); @@ -266,6 +182,8 @@ window.postMessage({ // 2. User presses "Connect Wallet" and the library display the discovered wallets +const selected_uuid = "350670db-19fa-4704-a166-e52e178b59d2" + // 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 @@ -290,6 +208,7 @@ const sessionRequest = { let sessionResult = {} window.addEventListener("message", (event) => { + if (event.targetOrigin !== wallets[selected_uuid].targetOrigin) return; if (event.data.id === sessionRequest.id) { // Get JSON-RPC response if (event.data.error) { @@ -300,7 +219,8 @@ window.addEventListener("message", (event) => { } }); -window.postMessage(sessionRequest); + +window.postMessage(sessionRequest, wallets[selected_uuid].targetOrigin); // 4. After the response was received by the Blockchain Library from the wallet @@ -309,7 +229,7 @@ window.postMessage(sessionRequest); const signingRequest = { id: 456, jsonrpc: "2.0", - method: "wallet_request", + method: "wallet_requestMethod", params: { // UUID from WalletData is used as SessionId sessionId: walletData.uuid, @@ -327,6 +247,7 @@ const signingRequest = { let signingResult = {} window.addEventListener("message", (event) => { + if (event.targetOrigin !== wallets[selected_uuid].targetOrigin) return; if (event.data.id === signingRequest.id) { // Get JSON-RPC response if (event.data.error) { @@ -337,14 +258,14 @@ window.addEventListener("message", (event) => { } }); -window.postMessage(signingRequest); +window.postMessage(signingRequest, wallets[selected_uuid].targetOrigin); ``` Logic from the Wallet Provider: -```js -// 1. Wallet Provider sets their WalletData and then listens to promptWallet message -// and also immediatelly posts a message with the WalletData as announceWallet type +```typescript +// 1. Wallet Provider sets their WalletData and then listens to prompt_wallet message +// and also immediatelly posts a message with the WalletData as wallet_announce type const walletData = { uuid: generateUUID(); // eg. "350670db-19fa-4704-a166-e52e178b59d2" name: "Example Wallet", @@ -373,10 +294,13 @@ window.addEventListener("message", (event) => { // 2. User presses "Connect Wallet" on the application webpage which will select UUID +const selected_uuid = "350670db-19fa-4704-a166-e52e178b59d2" + // 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 sessionRequest = {} +let sessionRequest = {} +let sessionOrigin = "" window.addEventListener("message", (event) => { if (event.data.method === "wallet_createSession") { @@ -384,11 +308,13 @@ window.addEventListener("message", (event) => { // if incoming requests match the WalletData UUID if (checkSupportedScopes(event.data.params)) { // prompt user to approve session + // persist the targetOrigin for sessionRequest + sessionOrigin = event.targetOrigin } } }); -window.postMessage({ +const sessionResponse = { id: sessionRequest.id, // 123 jsonrpc: "2.0", result: { @@ -408,67 +334,54 @@ window.postMessage({ expiry: "2024-06-06T13:10:48.155Z", } } -}); +} + +window.postMessage(sessionResponse, sessionOrigin); // 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 signingRequest = {} +let signingRequest = {} window.addEventListener("message", (event) => { + if (event.targetOrigin !== sessionOrigin) return; if (event.data.method === "wallet_createSession" && event.data.params.sessionId === walletData.uuid) { signingRequest = event.data.params } }); -window.postMessage({ +const signingResponse = { id: signingRequest.id // 456 jsonrpc: "2.0", result: "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" -}); +} + +window.postMessage(signingResponse, sessionOrigin); ``` ## 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: - -### 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 WalletData objects match. Applications and Libraries are expected to consider other potential methods that the WalletData 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 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 from being used as an obfuscation technique to hide malicious modifications to the page or to other wallets. +TODO ## 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. Wallet Providers can abstain from publishing `announceWallet` messages on every page load and wait for incoming `promptWallet` messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the `promptWallet` message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. +TODO ## 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 the EIP155 (Ethereum) ecosystem there are already interfaces for the discoverability of browser wallets through either legacy `window.ethereum` or [EIP-6963][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. - -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. - -The WalletData 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. +TODO ## Links - [EIP-6963][eip-6963] - Multi Injected Provider Discovery - [CAIP-27][caip-27] - Blockchain ID Specification - [CAIP-25][caip-25] - Blockchain ID Specification -- [CAIP-217][caip-217] - Provider Authorization Scopes -- [CAIP-222][caip-222] - Account ID Specification -- [CAIP-275][caip-275] - Domain Wallet Authentication +- [CAIP-282][caip-282] - Browser Wallet Discovery Interface [eip-6963]: https://eips.ethereum.org/EIPS/eip-6963 [caip-27]: https://chainagnostic.org/CAIPs/caip-27 [caip-25]: https://chainagnostic.org/CAIPs/caip-25 -[caip-217]: https://chainagnostic.org/CAIPs/caip-217 -[caip-222]: https://chainagnostic.org/CAIPs/caip-222 -[caip-275]: https://chainagnostic.org/CAIPs/caip-275 +[caip-282]: https://chainagnostic.org/CAIPs/caip-282 ## Copyright From c2b6ff099ff73a21e42311ee90fcff4d7fb5fa75 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 26 Jun 2024 23:43:02 +0100 Subject: [PATCH 23/28] update dates --- CAIPs/caip-294.md | 2 +- CAIPs/caip-295.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CAIPs/caip-294.md b/CAIPs/caip-294.md index b672c2f3..76dac1b7 100644 --- a/CAIPs/caip-294.md +++ b/CAIPs/caip-294.md @@ -5,7 +5,7 @@ author: Pedro Gomes (@pedrouid) discussions-to: https://github.com/ChainAgnostic/CAIPs/issues/294 status: Draft type: Standard -created: 2024-05-30 +created: 2024-06-26 requires: 25, 27, 282 --- diff --git a/CAIPs/caip-295.md b/CAIPs/caip-295.md index 79786ede..05c30dcb 100644 --- a/CAIPs/caip-295.md +++ b/CAIPs/caip-295.md @@ -5,7 +5,7 @@ author: Pedro Gomes (@pedrouid) discussions-to: https://github.com/ChainAgnostic/CAIPs/issues/295 status: Draft type: Standard -created: 2024-05-30 +created: 2024-06-26 requires: 25, 27, 282 --- From 3ea7bde4243a7128d79d446eada0b38a15133a7e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 2 Jul 2024 15:21:49 +0100 Subject: [PATCH 24/28] Update CAIPs/caip-282.md Co-authored-by: kewde --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 303599ac..a83701ee 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -43,7 +43,7 @@ Blockchain Library: A library or piece of software that assists a dapp to intera #### 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. +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. This discovery would use the following JSON-RPC request params: From 374e7104421da7d2c182ce722c2b5fe6b833ded0 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 2 Jul 2024 15:24:25 +0100 Subject: [PATCH 25/28] Update CAIPs/caip-282.md --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index a83701ee..360d551d 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -124,7 +124,7 @@ Here are some illustrative examples for both JSON-RPC request params: } -// Example for wallet_prompt +// Example for wallet_announce { id: 2, jsonrpc: "2.0" From 0f17dd3f811388910bebdfc7800a162874955bfa Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 2 Jul 2024 15:25:11 +0100 Subject: [PATCH 26/28] Update CAIPs/caip-282.md --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index 360d551d..c99bf2da 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -152,7 +152,7 @@ The use of SVG images introduces a cross-site scripting risk as they can include ## 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. Wallet Providers can abstain from publishing "Announce" messages on every page load and wait for incoming "Prompt" messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the "Prompt" message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. +Any form of wallet discoverability must always take into consideration wallet fingerprinting that can happen by malicious webpages or extensions that attempt to capture user information. Wallet Providers can abstain from publishing "Announce" messages on every page load and wait for incoming "Prompt" messages. Yet this opens the possibility for race conditions where Wallet Providers could be initialized after the "Prompt" message was published and therefore be undiscoverable. It is recommended that Wallet Providers offer this more "private connect" feature that users only enable optionally, rather than set by default. ## Backwards Compatibility From c1e833f9fa1bfbaedd4d7f8811397c5617afe875 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 2 Jul 2024 15:26:56 +0100 Subject: [PATCH 27/28] Update CAIPs/caip-282.md --- CAIPs/caip-282.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CAIPs/caip-282.md b/CAIPs/caip-282.md index c99bf2da..c3091372 100644 --- a/CAIPs/caip-282.md +++ b/CAIPs/caip-282.md @@ -68,7 +68,7 @@ Whenever a new Wallet Provider is discovered the Blockchain Library would index 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. -The only optional parameter is `scps` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. +The only optional parameter is `scopes` which is defined by CAIP-217 authorization specs that enables early discoverability and filtering of wallets based on RPC methods, notifications, documents and endpoints but also optional discovery of supported chains and even accounts. ```typescript // Defined by CAIP-217 From 02f119fb282a6b8be8d75eeeb613eb9ae6be051c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Aug 2024 16:18:06 -0500 Subject: [PATCH 28/28] add extensionId for externally_connectable --- CAIPs/caip-294.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/CAIPs/caip-294.md b/CAIPs/caip-294.md index 76dac1b7..41f48eef 100644 --- a/CAIPs/caip-294.md +++ b/CAIPs/caip-294.md @@ -127,6 +127,83 @@ window.addEventListener("caip294:wallet_prompt", (event) => { }); ``` +### Wallet Data + +The `walletData` object MUST include the following properties: + +- `uuid`: A unique identifier for the wallet instance. +- `name`: The name of the wallet. +- `icon`: An icon representing the wallet. +- `rdns`: The reverse domain name of the wallet provider. + +Additionally, the `walletData` object MAY include the following optional properties: + +- `extensionId`: The canonical extension ID of the wallet provider for the active browser. +- `scopes`: An object defining the authorization scopes supported by the wallet, as specified in CAIP-217. + +```typescript +interface WalletData { + // Required properties + uuid: string; + name: string; + icon: string; + rdns: string; + + // Optional properties + extensionId?: string; + scopes?: Caip217AuthorizationScopes; +} +``` + +Example of a `walletData` object with optional properties: + +```typescript +const walletData = { + uuid: "350670db-19fa-4704-a166-e52e178b59d2", + name: "Example Wallet", + icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", + rdns: "com.example.wallet", + extensionId: "abcdefghijklmnopqrstuvwxyz", + scopes: { + "eip155:1": { + methods: ["eth_signTransaction", "eth_sendTransaction"], + notifications: ["accountsChanged", "chainChanged"] + } + } +} +``` + +This `walletData` type is is a superset of `WalletAnnounceRequestParams` type described in the [CAIP-282][caip-282] standard, adding the optional `extensionId` property as it is only relevant for browser extension based wallets. + +### ExtensionId + +When the `extensionId` is included in the `walletData` object, it indicates that the wallet supports communication via the browser's `externally_connectable` API. In this case: + +1. The dapp MUST use the `extensionId` to establish a connection with the wallet using the `externally_connectable` browser API. +2. All subsequent communication with the wallet (the "session") SHOULD be conducted over the `externally_connectable` API using `runtime.connect()` and `runtime.sendMessage()`. +3. The dapp MUST NOT use the injected provider for communication when `extensionId` is present. + +Example of establishing a connection and sending a message: + +```javascript +const port = chrome.runtime.connect(walletData.extensionId); + +port.onMessage.addListener((message) => { + // Handle incoming messages +}); + +port.postMessage({ + id: 1, + jsonrpc: "2.0", + method: "wallet_createSession", + params: { + // ... session parameters ... + } +}); +``` + +If the `extensionId` is not present in the `walletData` object, the dapp SHOULD assume that communication will occur through the traditional injected provider method. + #### 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.