stacksgov / sips

Community-submitted Stacks Improvement Proposals (SIPs)
135 stars 81 forks source link

Wallet client API #117

Open kyranjamie opened 1 year ago

kyranjamie commented 1 year ago

There should be a standard describing the style of API used by wallet clients.

@friedger pushed a draft SIP a while back describing the existing wallet API. Though, more recently, there's been some discussion about moving away from the existing JWT-style API towards something more consistent with other ecosystems, like an RPC style API.

I suggest a SIP that describes a feature-agnostic RPC API that works cross-platform: Web Extensions, mobile apps, CLIs etc, similar to that of EIP-1102.

SIP Draft # Wallet Client API This SIP describes a simpler standard for apps to interface with wallet clients. ## Abstract This proposal outlines a uniform manner with which apps can interface with wallet clients. It recommends to minimise the surface area of available APIs, and makes design decisions similar to those in other crypto ecosystems. ## Introduction No standard exists describing how apps interact with Stacks-compatible wallets. Presently, ecosystem wallets adhere to an undocumented set of commands that wallets should implement. A well-documented API, that’s familiar to developers from other ecosystems, benefits both app and wallet developers. The purpose of this SIP is to describe the outline of an API that can support multiple environments including, but not limited to, Web Extensions, mobile wallets, CLIs etc. It does not describe the individual methods that clients may commonly support. ## Specification Two methods are described. The methods can be typed such that the return value corresponds to the method name passed. ### `request` `request(method: string, params?: object[]): Promise` A `request` method is accepted by clients, following the [JSON RPC standard](https://www.jsonrpc.org/specification) _Example_ ```ts const accounts = await Provider.request('stx_requestAccounts'); ``` ### `listen` `listen(event: string): Observable` A `listen` method accepts an event parameter. It returns an `Observable`, following the [TC39 specification](https://github.com/tc39/proposal-observable), that consumers can subscribe and unsubscribe to. _Example_ ```ts Provider.listen('networkChange').subscribe(network => toast('Switched network')) ``` ## Related Work [https://eips.ethereum.org/EIPS/eip-1102](https://eips.ethereum.org/EIPS/eip-1102) [https://eips.ethereum.org/EIPS/eip-1474](https://eips.ethereum.org/EIPS/eip-1474) [https://eips.ethereum.org/EIPS/eip-2255](https://eips.ethereum.org/EIPS/eip-2255) ## Backwards Compatibility Wallet clients should continue to support the existing methods until a time where a sufficient majority of apps adhere to the new standard. They may warn developers of the newer format, encouraging developer to migrate. ## Activation This SIP is considered activated when at least two wallet clients support this API.

Happy to work on this with anyone that wants to collaborate.

cc/ @janniks @yknl @markmhx @fbwoolf @edu-stx

Edit: March 2023 update

With the suggestions/ideas from this SIP @janniks and I have started http://btckit.org, which aims to push this effort and form a community-driven, wallet-agnostic standard.

janniks commented 1 year ago

Leaving my thoughts so far:

  1. I like the proposal of adding two simple flexible/generic methods that can be expanded by wallets without needing a SIP for everything. e.g. wallets could prefix beta features (similar to http headers or css rules) and alias them once a common standard is found/implemented by multiple wallets. And even if this is the standardized protocol of talking to a wallet that doesn't mean the interface exposed to application developer can't stay the same (i.e. we don't need to concern the web-app developer with available strings, unless they're playing with beta features and know what they're doing).
  2. I'm not too sure about the usage of Observable. I would prefer describing a native (or more seasoned) JS pattern like a simply listen(event: string, callback: (error: Error, payload: any) => void) via callback (which respective connect/client applications could wrap with their favorite Observable implementation, but don't have too).
  3. I like the move away from the cluttered JWT wrapped protocol, in place currently. This simplified API can be used for secure contexts (eg talking to desktop browser extensions), while an additional SIP could target adding sessions (e.g. over relays, similar to wallet-connect, or implementing wallet-connect). Once a secure session is established the session instance could expose the same object as this Wallet API proposal.
  4. I'm in favor of short SIPs which can stack on top of each other well (similar to many NIPs eg) -- so it's nice to see a short and concise SIP which could standardize the parameters of a send_transaction request in a follow-up SIP.
  5. params might need stricter types like params: jsonobject | json[] ?
  6. It might make sense to at some point include (in this SIP or separately) how to serialize certain data-types, so they're always the same during transit. e.g. bytes as a 0x prefixed string, bigint as string?

Would love to hear @yknl thoughts, especially regarding wallet-connect

janniks commented 1 year ago

Wallet Connect docs from Xverse: https://docs.xverse.app/wallet-connect/reference/api_reference

whoabuddy commented 1 year ago

FYI - cleaned up some spam comments

janniks commented 1 year ago

Leaving my thought and drafts here for now... Based on @kyranjamie's work -- Maybe we should use a more collaborative doc for this, if folks even agree. I don't want to overstep 🤷‍♂


SIP Draft — Wallet Client API

This SIP describes a simpler standard for apps to interface with wallet clients.

Abstract

This proposal outlines a uniform manner with which apps can interface with wallet clients.

It recommends to minimise the surface area of available APIs, and makes design decisions similar to those in other crypto ecosystems.

Introduction

No standard exists describing how apps interact with Stacks-compatible wallets. Presently, ecosystem wallets adhere to an undocumented set of commands that wallets should implement. A well-documented API, that’s familiar to developers from other ecosystems, benefits both app and wallet developers.

The purpose of this SIP is to describe the outline of an API that can support multiple environments including, but not limited to, Web Extensions, mobile wallets, CLIs etc. It does not describe the individual methods that clients may commonly support.

Specification — Wallet Client

A window.WalletClient object can be provided by browsers or web extensions so websites/web-apps can check for it's availability and use the provided methods below.

requestrequired

request(method: string, params?: JSONObject): Promise<T>

A request method is accepted by clients, following the JSON RPC standard.

listenrequired

listen(event: string, callback: (result) => void): void

A listen method accepts an event parameter.

The injected provider should remain lightweight, unopinionated, and use proven JavaScript conventions. Wrapper libraries (e.g. connect, micro-stacks/client) could choose to wrap the listen interface in an TC 39 Observable or any other interface.

Examples

request

const accounts = await window.WalletClient.request("stx_requestAccounts");
const accounts = await window.WalletClient.request("stx_sendTransaction", {
  ...
});

listen

window.WalletClient.listen("networkChange", (network) => {
  alert("Switched network");
});

Notes

Event/Method Payloads

This SIP intentionally does not describe what actions/events examples and conventions can be.

Sessions

The approach described in this SIP is session-less and assumes a safe/encrypted communication between the wallet and the web-app.

Comparison

Current State


Todo & Questions


Sibling SIPs

1: [Stacks] Types, Params Interface, Data Type Serialization

It's up to the wallet what to respond to on RPC calls and how to parse data, etc.

But it is prefered to only use named-object params JSONObject.

Some proven defaults are recommended:

Request Methods

Events

Params

Payload independany fields

field type examples
postConditions
postConditionMode
nonce
fee

Type Serialization/Deserialization

WalletConnect

The same params defined for Stacks SIPs should be re-used for adapters like WalletConnect. This way wallets can share code and provide the same interface directly or via WalletConnect sessions.

2: [Bitcoin] Types

3: Multi-Chain / Layer -- Client

Provide StacksClient under window.WalletClient which can expose multiple layers of bitcoin. This way

window.WalletClient.Stacks ...
                 // ↑
                 // The same as StacksClient, but can group more functionality (e.g. BTC transfers)

Related Work

svntax commented 1 year ago

This proposal would help greatly in implementing wallet connecting for game engines with web exports, like Unity and Godot. The current JWT-based API is difficult to work with, so I support this change.

Hero-Gamer commented 1 year ago

Xverse launched open source wallet connect standard called "Sats Connect", just to make sure it's captured in this conversation. Documentation here: https://docs.xverse.app/sats-connect/ Twitter post here: https://twitter.com/xverseapp/status/1635592234623021056

markmhendrickson commented 1 year ago

Note that the http://btckit.org/ standard we've started using for Hiro Wallet includes the .request approach in this SIP issue from February.

Sats Connect chose not to apply such a .request approach when it was released a couple weeks ago despite our hope that it would be adopted among wallets generally, and we don't seem to have heard anything about it from the Xverse team.

Sats Connect was also promoted as a new API / library, not a standard per se, so it's not clear whether as a library it can incorporate .request going forward as well, or whether it proposes an alternative approach for specific reasons. We'd love to hear about them if there's a rationale to going in a different direction.