Skip to content

Commit 37f8f42

Browse files
eric-stacksgitbook-bot
authored andcommitted
GITBOOK-42: add Wallet Implementation page
1 parent c012c5f commit 37f8f42

File tree

6 files changed

+198
-0
lines changed

6 files changed

+198
-0
lines changed
97.2 KB
Loading
211 KB
Loading
327 KB
Loading
300 KB
Loading

docs/build/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
* [Message Signing](stacks-connect/message-signing.md)
7474
* [Migration Guide](stacks-connect/migration-guide.md)
7575
* [Wallet Support](stacks-connect/wallet-support.md)
76+
* [Wallet Implementation](stacks-connect/wallet-implementation.md)
7677

7778
## Post-Conditions
7879

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
description: Support Stacks Connect in your own wallet
3+
---
4+
5+
# Wallet Implementation
6+
7+
<div data-with-frame="true"><figure><img src="../.gitbook/assets/wallet-implementation.png" alt=""><figcaption></figcaption></figure></div>
8+
9+
Connect provides a streamlined way for wallets to integrate with dapps by using a simple, direct RPC-based protocol, avoiding unnecessary abstraction layers. It defines a clear wallet provider interface and discovery mechanism, enabling consistent, conflict-free wallet connections. This approach makes it easier for applications to integrate wallets and for anyone to build a wallet that is reliably discoverable by Connect-enabled dapps.
10+
11+
### Discovery Mechanism
12+
13+
**Enable Your Custom Wallet to be Detected by Stacks Apps**
14+
15+
<div data-with-frame="true"><figure><img src="../.gitbook/assets/custom-wallet-connect-modal.png" alt=""><figcaption></figcaption></figure></div>
16+
17+
We will show you how your wallet can interact with incoming JSON RPC 2.0 requests and responses to handle modern Connect methods in order to connect to apps. But first, you’ll want to make sure you have a good understanding of the different context script standards of a [Chrome extension](https://developer.chrome.com/docs/extensions). The context scripts mainly consist of your popup script, background script, and content script.
18+
19+
**3 scripts of a Chrome extension:**
20+
21+
* **Popup**: This is the main script that handles the visual UI of the actual popup modal when interacting with an extension.
22+
* **Background**: This script allows your extension to hand off logic that may require intensive computation or for dealing with secure data.
23+
* **Content**: This allows your extension to interact with the web page itself.
24+
25+
In your content script, which enables you to run scripts on the web page a user is currently on, you’ll want to “inject” a `StacksProvider` object type into the global `window` object of the web page. It’s important to note that this must be handled by your extension’s content script, which should automatically load anytime you land on a webpage. This injected object is what will allow web apps to directly interact with your wallet. It’s like your wallet extension saying, “Hey! I’m available to communicate with your app, let’s connect!”&#x20;
26+
27+
The `StacksProvider` object needs to at least have a `.request` method that takes in the name of a string literal method, and an parameters object.
28+
29+
{% code title="injection.js" expandable="true" %}
30+
```typescript
31+
// --snip--
32+
33+
window.MyProvider = {
34+
async request(method, params) {
35+
// Somehow communicate with the wallet (e.g. via events)
36+
37+
// Recommendation: Create a JSON RPC 2.0 request object
38+
// https://www.jsonrpc.org/specification
39+
40+
return Promise.resolve({
41+
// Respond with a JSON RPC 2.0 response object
42+
id: crypto.randomUUID(), // required, same as request
43+
jsonrpc: '2.0', // required
44+
45+
// `.result` is required on success
46+
result: {
47+
// object matching specified RPC methods
48+
},
49+
50+
// `.error` is required on error
51+
error: {
52+
// Use existing codes from https://www.jsonrpc.org/specification#error_object
53+
code: number, // required, integer
54+
message: string, // recommended, single sentence
55+
data: object, // optional
56+
},
57+
});
58+
},
59+
isMyWallet: true, // optional, a way of identifying the wallet for developers
60+
};
61+
62+
// --snip--
63+
```
64+
{% endcode %}
65+
66+
This `StacksProvider` object type could be named anything. In the example above, it’s named `MyProvider`.&#x20;
67+
68+
From here, web apps can directly call your wallet extension provider via `window.MyProvider` directly, and you don’t even need to use the Stacks Connect library. However, your wallet app would need to manually handle other important implementation details, such as the storage of the wallet info and individual method calling.&#x20;
69+
70+
But with the Connect library, apps don’t have to manually roll their own methods and implementations. The Connect library will handle all those functionalities for the app.
71+
72+
In order for you to make your wallet provider object (from the previous section) be discoverable by the Connect modal UI wallet selector used by frontend apps, you’ll need to then pass it into a separate `wbip_providers` array on the `window` object. The `wbip_providers` array is a new standard set forth by [WBIP004](https://wbips.netlify.app/wbips/WBIP004).
73+
74+
Any wallet that registers their provider in this array is declaring that they are conforming to the WBIP standards, which are a set of specifications for web apps and client providers to facilitate communication with Bitcoin-related apps. Wallets SHOULD register their provider information under `window.wbip_providers` to be discoverable by websites/libraries expecting this WBIP.
75+
76+
{% code title="injection.js" expandable="true" %}
77+
```typescript
78+
// --snip--
79+
80+
window.wbip_providers = window.wbip_providers || [];
81+
window.wbip_providers.push({
82+
// `WbipProvider` type
83+
/** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */
84+
id: 'MyProvider',
85+
/** The name of the provider, as displayed to the user */
86+
name: 'My Wallet';
87+
/** The data URL of an image to show (e.g. `data:image/png;base64,iVBORw0...`) @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs */
88+
icon?: 'data:image/png;base64,iVBORw0...';
89+
/** Web URL of the provider */
90+
webUrl?: 'https://mywallet.example.com';
91+
92+
// Additional URLs
93+
chromeWebStoreUrl?: string;
94+
mozillaAddOnsUrl?: string;
95+
googlePlayStoreUrl?: string;
96+
iOSAppStoreUrl?: string;
97+
});
98+
99+
// --snip--
100+
```
101+
{% endcode %}
102+
103+
Literally injecting these scripts can come from your content script as shown below. You could leverage the content.js script for injecting the `injection.js` into the document page and forwarding messages between the document page and the background script. Setup is dependent on your architecture.
104+
105+
{% code title="content.js" %}
106+
```typescript
107+
// --snip--
108+
109+
const script = document.createElement("script");
110+
script.src = chrome.runtime.getURL("injection.js");
111+
script.type = "module";
112+
document.head.prepend(script);
113+
114+
// --snip--
115+
```
116+
{% endcode %}
117+
118+
### Handling Method Requests and Responses <a href="#handling-method-requests-and-responses" id="handling-method-requests-and-responses"></a>
119+
120+
**Enable your wallet to handle requests from the frontend app**
121+
122+
Structuring the manner in which your wallet handles methods internally is up to your discretion (most methods can be properly handled by methods from [@stacks/transactions](https://docs.hiro.so/stacks/stacks.js/packages/transactions)), but receiving and responding to messages should adhere to the JSON RPC 2.0 standard and data types based on the string literal methods of the incoming request.
123+
124+
Let’s take the most basic function of connecting. From the Connect modal UI wallet selector, once a user clicks on the `connect` button of your wallet, it will invoke the string literal method of `getAddresses`, which accepts an optional parameter of `network`.
125+
126+
<div data-with-frame="true"><figure><img src="../.gitbook/assets/wallet-communication-flow.png" alt=""><figcaption><p>Communication flows are based off of standards like WBIP and SIP-030 to allow wallets to communicate with apps in a more simplified and flexible way.</p></figcaption></figure></div>
127+
128+
Once your wallet receives this JSON RPC 2.0 request message, it needs to handle the request and then return a response that conforms to the return type for `getAddresses`.
129+
130+
Using the `MethodParams` and `MethodResult` type helpers from the Connect library can help you here. Here’s a simplified example of how your wallet should handle the string literal method of `getAddresses`, which allows a standard connection between your wallet and app.
131+
132+
{% code expandable="true" %}
133+
```typescript
134+
import { type MethodResult, type MethodParams } from "@stacks/connect";
135+
136+
async function handleGetAddresses(payload: JsonRpcRequest) {
137+
let params: MethodParams<"getAddresses"> = payload.params;
138+
139+
// handle generation of account addresses to return back to the app
140+
141+
let result: MethodResult<"getAddresses"> = {
142+
addresses: [
143+
{
144+
symbol: "BTC",
145+
address: btcP2PKHAddress,
146+
publicKey: pubKey,
147+
},
148+
{
149+
symbol: "BTC",
150+
address: btcP2TRAddress,
151+
publicKey: pubKey,
152+
},
153+
{
154+
symbol: "STX",
155+
address: stxAddress,
156+
publicKey: pubKey,
157+
}
158+
]
159+
};
160+
161+
return result
162+
}
163+
```
164+
{% endcode %}
165+
166+
You can also add your own unstandardized methods to your wallet. However, the minimum recommended methods to handle basic wallet functions are standardized and include:
167+
168+
* `getAddresses`
169+
* `sendTransfer`
170+
* `signPsbt`
171+
* `stx_getAddresses`
172+
* `stx_transferStx`
173+
* `stx_callContract`
174+
* `stx_signMessage`
175+
* `stx_signStructuredMessage`
176+
177+
### Stacks Wallet Template
178+
179+
**Build your own Stacks wallet with the Wallet Template in the Hiro Platform**
180+
181+
<div data-with-frame="true"><figure><img src="../.gitbook/assets/wallet-extension-template.png" alt=""><figcaption></figcaption></figure></div>
182+
183+
This template is a Chrome extension that comes with basic wallet functionalities, such as generating Stacks and Bitcoin addresses, changing accounts, and importing of external mnemonic seed phrases. Using this template as a starting point, you can build on this template to add other wallet features, such as displaying Stacks NFTs, fetching [Ordinals](https://www.hiro.so/ordinals-api) or [Runes](https://www.hiro.so/runes-api) balances with our dedicated APIs, securing of user mnemonic seed phrases, and much more.
184+
185+
It’s important to have an ecosystem that boasts a plethora of diverse wallet providers for different use cases, and learning how to build a wallet is a great entry point to Web3 and the Stacks ecosystem. Check out this [article](https://www.hiro.so/blog/an-intro-to-web3-wallets-for-web3-founders) to learn more about the importance of web3 wallets for web3 founders.
186+
187+
Head to the [Hiro Platform](https://platform.hiro.so/) to start building with this template.
188+
189+
***
190+
191+
### Additional Resources
192+
193+
* \[[Hiro Platform](https://platform.hiro.so/templates/wallet-extension)] Stacks Wallet Extension Template
194+
* \[[Hiro YT](https://www.youtube.com/watch?v=PdluvfFPWoU)] Build Your Own Bitcoin L2 Wallet Browser Extension
195+
* \[[Github repo](https://github.com/hirosystems/platform-template-stacks-wallet)] Open-source repo of the Stacks wallet extension template
196+
* \[[WBIP](https://wbips.netlify.app/)] Stacks Wallet BIPs
197+
* \[[SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)] Definition of a Modern Stacks Wallet Interface Standard

0 commit comments

Comments
 (0)