Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Wallet client API #117

Open
kyranjamie opened this issue Feb 4, 2023 · 7 comments
Open

Wallet client API #117

kyranjamie opened this issue Feb 4, 2023 · 7 comments

Comments

@kyranjamie
Copy link

kyranjamie commented Feb 4, 2023

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<T>

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

Example

const accounts = await Provider.request('stx_requestAccounts');

listen

listen(event: string): Observable<T>

A listen method accepts an event parameter. It returns an Observable, following the TC39 specification, that consumers can subscribe and unsubscribe to.

Example

Provider.listen('networkChange').subscribe(network => toast('Switched network'))

Related Work

https://eips.ethereum.org/EIPS/eip-1102

https://eips.ethereum.org/EIPS/eip-1474

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
Copy link
Contributor

janniks commented Feb 8, 2023

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
Copy link
Contributor

janniks commented Feb 8, 2023

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

@stacksgov stacksgov deleted a comment from BhinbahadurUK Feb 8, 2023
@stacksgov stacksgov deleted a comment from BhinbahadurUK Feb 8, 2023
@stacksgov stacksgov deleted a comment from BhinbahadurUK Feb 8, 2023
@whoabuddy
Copy link
Member

FYI - cleaned up some spam comments

@janniks
Copy link
Contributor

janniks commented Feb 19, 2023

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.

  • The method parameter describes an action for the wallet to execute.
  • The optional params parameter offers arbitrary parameters to be passed as a JSON object.

listenrequired

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

A listen method accepts an event parameter.

  • The event parameter describes a wallet event. On the occurence of an event described by the event parameter, the wallet should call the provided callback.
  • The callback 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

  • Safer to have individual RPCs for requesting specific information. E.g. a user might not want to share their mainnet address when sharing a testnet address.
  • Better to have a smaller surface area, which doesn't grow stale as the ecosystem evolves (as we have seen in the past with many unused legacy features not needed any more).
  • Better suited for multi-chain (e.g. adding BTC transfers)

Todo & Questions

  • params always named or allow ordered params (without name)
  • Standardize errors? What does a failure look like, what does an abort/cancel look like? What do missing methods look like?
  • Callback parameter -- Can listen have errors?
  • Add getter for retrieving available methods? Dangers? Fingerprinting?
  • JSON RPC notification? Or generate id (uuid) for each request?
  • JSON RPC response structure? (error, id etc?)
  • Use MUST SHOULD MAY RFC language?
  • As defined in JSON RPC, the client should set "jsonrpc": "2.0" and "id": new Uuid() for requests ??
  • Nostr compatible? different namespace? etc.

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

  • stx_stxTransfer
  • stx_ftTransfer
  • stx_nftTransfer
  • stx_contractCall
  • stx_contractDeploy

Events

  • 'stx_networkChanged'

  • 'stx_accountsChanged'

  • stx_connect

  • stx_disconnect

Params

  • recipient = Stacks address string
  • version = semver string; used for versioning the params structure (e.g. a wallet could provide an experimental request method and later use the version parameter to deprecate the legacy structure)

Payload independany fields

field type examples
postConditions
postConditionMode
nonce
fee

Type Serialization/Deserialization

  • Bytes shall be encoded as strings prefixed by 0x

  • BigInts shall be encoded as strings parseable by the BigInt() JS constructor.

  • fee, from attributes, which can be inferred or selected from the wallet are optional

  • Addresses?

  • fee, nonce = BigInt

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
Copy link

svntax commented Mar 12, 2023

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
Copy link
Contributor

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
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants