Wallet client API
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.
Leaving my thoughts so far:
- 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).
- I'm not too sure about the usage of
Observable. I would prefer describing a native (or more seasoned) JS pattern like a simplylisten(event: string, callback: (error: Error, payload: any) => void)via callback (which respective connect/client applications could wrap with their favoriteObservableimplementation, but don't have too). - 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.
- 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_transactionrequest in a follow-up SIP. paramsmight need stricter types likeparams: jsonobject | json[]?- 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
0xprefixed string, bigint as string?
Would love to hear @yknl thoughts, especially regarding wallet-connect
Wallet Connect docs from Xverse: https://docs.xverse.app/wallet-connect/reference/api_reference
FYI - cleaned up some spam comments
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.
request — required
request(method: string, params?: JSONObject): Promise<T>
A request method is accepted by clients, following the JSON RPC standard.
- The
methodparameter describes an action for the wallet to execute. - The optional
paramsparameter offers arbitrary parameters to be passed as a JSON object.
listen — required
listen(event: string, callback: (result) => void): void
A listen method accepts an event parameter.
- The
eventparameter describes a wallet event. On the occurence of an event described by theeventparameter, the wallet should call the provided callback. - The
callbackparameter ...
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
- [ ]
paramsalways 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
listenhave errors? - [ ] Add getter for retrieving available methods? Dangers? Fingerprinting?
- [ ] JSON RPC
notification? Or generateid(uuid) for each request? - [ ] JSON RPC response structure? (
error,idetc?) - [ ] 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_stxTransferstx_ftTransferstx_nftTransferstx_contractCallstx_contractDeploy
Events
-
'stx_networkChanged'
-
'stx_accountsChanged'
-
[ ]
stx_connect -
[ ]
stx_disconnect
Params
recipient= Stacks addressstringversion= semverstring; used for versioning the params structure (e.g. a wallet could provide an experimental request method and later use theversionparameter 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,fromattributes, 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
- https://eips.ethereum.org/EIPS/eip-1102
- https://eips.ethereum.org/EIPS/eip-1474
- https://eips.ethereum.org/EIPS/eip-2255
- https://github.com/nostr-protocol/nips/blob/master/07.md
- https://walletconnect.com
- https://docs.walletconnect.com/2.0/specs/
- https://docs.walletconnect.com/2.0/specs/clients/sign/client-api
- https://docs.walletconnect.com/2.0/javascript/providers/ethereum#events
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.
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
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.