Skip to content

Commit 1323dc0

Browse files
authored
fix: readme updates and fixes (#436)
1 parent fb0b4bf commit 1323dc0

File tree

8 files changed

+242
-36
lines changed

8 files changed

+242
-36
lines changed

.changeset/serious-planets-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@stacks/connect': patch
3+
---
4+
5+
Add `approvedProviderIds` options to filter allowed wallets

.changeset/twelve-eels-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@stacks/connect': patch
3+
---
4+
5+
Export error types

README.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,23 @@
22
<img src="/.github/img/banner.svg" alt="Stacks Connect">
33
</div>
44

5-
Connect is a JavaScript library for building web applications connected to [Stacks](https://stacks.co).
5+
## ⚡️ Building a Stacks-enabled web app?
66

7-
<div align="center">
8-
<code><a href="./packages/connect">@stacks/connect</a></code> •
9-
<code><a href="./packages/connect-react">@stacks/connect-react</a></code> •
10-
<code><a href="./packages/connect-ui">@stacks/connect-ui</a></code>
11-
</div>
12-
13-
> See methods and migration notes in the [`@stacks/connect` documentation](./packages/connect).
7+
Head over to the [`@stacks/connect` README](https://github.com/hirosystems/connect/tree/main/packages/connect).
148

159
---
1610

17-
## ⚡️ Installation
18-
19-
Use your favorite package manager to install `@stacks/connect` in your project.
20-
Follow the **Getting Started** section of the [`@stacks/connect` README](https://github.com/hirosystems/connect/tree/main/packages/connect).
21-
22-
> Or use one of our starter-templates to bootstrap a fresh project already including connect using the [command-line](https://github.com/hirosystems/stacks.js-starters) locally via `npm create stacks`
11+
## Development Notes
2312

24-
## 📦 Packages
13+
### Packages
2514

2615
This repository includes three packages:
2716

2817
- [`@stacks/connect`](./packages/connect): The one-stop-shop tool for letting web-apps interact with Stacks web wallets.
2918
- [`@stacks/connect-ui`](./packages/connect-ui): A web-component UI for displaying an intro modal in Stacks web-apps during authentication _(used in the background by `@stacks/connect`)_.
3019
- ~~[`@stacks/connect-react`](./packages/connect-react): A wrapper library for making `@stacks/connect` use in React even easier~~
3120

32-
## 🛠️ Wallet Implementation Guide
21+
### Wallet Implementation Guide
3322

3423
Wallets implement a "Provider" interface.
3524
The latest spec uses a simple JS Object exposing a `.request(method: string, params?: object)` method.
@@ -86,7 +75,7 @@ window.wbip_providers.push({
8675
});
8776
```
8877

89-
### JSON RPC 2.0
78+
#### JSON RPC 2.0
9079

9180
Wallets may add their own unstandardized methods.
9281
However, the minimum recommended methods are:

packages/connect/README.md

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,19 @@ npm install @stacks/connect@latest
5757

5858
- `request` follows the pattern `request(method: string, params: object)`, see [Usage](#usage) for more details
5959
- `request` is an async function, so replace the `onFinish` and `onCancel` callbacks with `.then().catch()` or `try & await`
60+
- e.g., `showConnect()`, `authenticate()``connect()`
61+
- e.g., `useConnect().doContractCall({})``request("stx_callContract", {})`
62+
- e.g., `openContractDeploy()``request("stx_deployContract", {})`
6063

61-
3. Switch from `showConnect` or`authenticate` to `connect()` methods
64+
1. Switch from `showConnect` or`authenticate` to `connect()` methods
6265

6366
- `connect()` is an alias for `request({forceWalletSelect: true}, 'getAddresses')`
6467
- `connect()` by default caches the user's address in local storage
6568

66-
4. Switch from `UserSession.isSignedIn()` to `isConnected()`
67-
5. Switch from `UserSession.signUserOut()` to `disconnect()`
68-
6. Remove code referencing deprecated methods (`AppConfig`, `UserSession`, etc.)
69-
7. Remove the `@stacks/connect-react` package.
69+
2. Switch from `UserSession.isSignedIn()` to `isConnected()`
70+
3. Switch from `UserSession.signUserOut()` to `disconnect()`
71+
4. Remove code referencing deprecated methods (`AppConfig`, `UserSession`, etc.)
72+
5. Remove the `@stacks/connect-react` package.
7073
- You may need to manually reload a component to see local storage updates.
7174
- No custom hooks are needed to use Stacks Connect anymore.
7275
- We are working on a new `@stacks/react` package that will make usage even easier in the future (e.g. tracking transaction status, reloading components when a connection is established, updating the page when the network changes, and more).
@@ -76,7 +79,7 @@ npm install @stacks/connect@latest
7679
Previously, the `UserSession` class was used to access the user's addresses and data, which abstracted away the underlying implementation details.
7780
Now, the `request` method is used to directly interact with the wallet, giving developers more explicit control and clarity over what's happening under the hood.
7881
This manual approach makes the wallet interaction more transparent and customizable.
79-
Developer can manually manage the currently connected user's address in e.g. local storage, jotai, etc. or use the `connect()` method to cache the address in local storage.
82+
Developer can manually manage the currently connected user's address in e.g. local storage, jotai, etc. or use the `connect()`/`request()` method to cache the address in local storage.
8083

8184
> [!IMPORTANT]
8285
> For security reasons, the `8.x.x` release only returns the current network's address (where previously both mainnet and testnet addresses were returned).
@@ -333,6 +336,59 @@ const response = await request('stx_signStructuredMessage', {
333336
// }
334337
```
335338

339+
## Error Handling
340+
341+
The `request` method returns a Promise, allowing you to handle errors using standard Promise-based error handling patterns. You can use either `try/catch` with `async/await` or the `.catch()` method with Promise chains.
342+
343+
### Using try/catch with async/await
344+
345+
```ts
346+
import { request } from '@stacks/connect';
347+
348+
try {
349+
const response = await request('stx_transferStx', {
350+
amount: '1000',
351+
recipient: 'SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN',
352+
});
353+
// SUCCESS
354+
console.log('Transaction successful:', response.txid);
355+
} catch (error) {
356+
// ERROR
357+
console.error('Wallet returned an error:', error);
358+
}
359+
```
360+
361+
## Compatibility
362+
363+
The `request` method by default adds a layer of auto-compatibility for different wallet providers.
364+
This is meant to unify the interface where wallet providers may not implement methods and results the same way.
365+
366+
| Method | | Notes |
367+
| --------------------------- | --- | ---------------------------------------------------------------------------------------------------- |
368+
| `getAddresses` | 🔵 | <sub>Maps to `wallet_connect` for Xverse-like wallets</sub> |
369+
| `sendTransfer` | 🔵 | <sub>Converts `amount` to number for Xverse, string for Leather</sub> |
370+
| `signPsbt` | 🟡 | <sub>Transforms PSBT format for Leather (base64 to hex) with lossy restructure of `signInputs`</sub> |
371+
| `stx_getAddresses` | 🔵 | <sub>Maps to `wallet_connect` for Xverse-like wallets</sub> |
372+
| `stx_getAccounts` | 🟢 | |
373+
| `stx_getNetworks` | 🟢 | |
374+
| `stx_transferStx` | 🟢 | |
375+
| `stx_transferSip10Ft` | 🟢 | |
376+
| `stx_transferSip9Nft` | 🟢 | |
377+
| `stx_callContract` | 🔵 | <sub>Transforms Clarity values to hex-encoded format for compatibility</sub> |
378+
| `stx_deployContract` | 🔵 | <sub>Transforms Clarity values to hex-encoded format for compatibility</sub> |
379+
| `stx_signTransaction` | 🔵 | <sub>Transforms Clarity values to hex-encoded format for compatibility</sub> |
380+
| `stx_signMessage` | 🔵 | <sub>Transforms Clarity values to hex-encoded format for compatibility</sub> |
381+
| `stx_signStructuredMessage` | 🔵 | <sub>Transforms Clarity values to hex-encoded format for compatibility</sub> |
382+
| `stx_updateProfile` | 🟢 | |
383+
| `stx_accountChange` (event) | 🟢 | |
384+
| `stx_networkChange` (event) | 🟢 | |
385+
386+
- 🟢 No overrides needed for any wallet
387+
- 🔵 Has compatibility overrides that maintain functionality
388+
- 🟡 Has breaking overrides that may lose some information
389+
390+
> To disable this behavior, you can set the `enableOverrides` option to `false` or use the `requestRaw` method detailed below.
391+
336392
## Advanced Usage
337393

338394
### `request`
@@ -346,10 +402,14 @@ import { request } from '@stacks/connect';
346402
const response = await request(
347403
{
348404
provider?: StacksProvider; // Custom provider to use for the request
349-
defaultProviders?: WbipProvider[]; // Default wallets to display in modal
405+
350406
forceWalletSelect?: boolean; // Force user to select a wallet (default: false)
351407
persistWalletSelect?: boolean; // Persist selected wallet (default: true)
352408
enableOverrides?: boolean; // Enable provider compatibility (default: true)
409+
enableLocalStorage?: boolean; // Store address in local storage (default: true)
410+
411+
defaultProviders?: WbipProvider[]; // Default wallets to display in modal
412+
approvedProviderIds?: string[]; // List of approved provider IDs to show in modal
353413
},
354414
'method',
355415
params
@@ -363,6 +423,20 @@ const response = await request('method', params);
363423
> For example, it handles converting numeric types between string and number formats as needed by different wallets, and remaps certain method names to match wallet-specific implementations.
364424
> This ensures consistent behavior across different wallet providers without requiring manual adjustments.
365425
426+
> The `approvedProviderIds` option allows you to filter which wallet providers are shown in the connect modal.
427+
> This is useful when you want to limit the available wallet options to specific providers.
428+
> For example, you might only want to support Leather wallet:
429+
>
430+
> ```ts
431+
> connect({ approvedProviderIds: ['LeatherProvider'] });
432+
> ```
433+
>
434+
> Or multiple specific wallets:
435+
>
436+
> ```ts
437+
> connect({ approvedProviderIds: ['LeatherProvider', 'xverse'] });
438+
> ```
439+
366440
### `requestRaw`
367441
368442
The `requestRaw` method provides direct access to wallet providers without the additional features of `request`:
@@ -376,6 +450,40 @@ const response = await requestRaw(provider, 'method', params);
376450
> Note: `requestRaw` bypasses the UI wallet selector, automatic provider compatibility fixes, and other features that come with `request`.
377451
> Use this when you need more manual control over the wallet interaction process.
378452
453+
## Support
454+
455+
Here's a list of methods and events that are supported by popular wallets:
456+
457+
| Method | Leather | Xverse-like |
458+
| --------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------ |
459+
| `getAddresses` | 🟡 <sub>No support for experimental purposes</sub> | 🟡 <sub>Use `wallet_connect` instead</sub> |
460+
| `sendTransfer` | 🟡 <sub>Expects `amount` as string</sub> | 🟡 <sub>Expects `amount` as number</sub> |
461+
| `signPsbt` | 🟡 <sub>Uses signing index array only</sub> | 🟡 <sub>Uses `signInputs` record instead of array</sub> |
462+
| `stx_getAddresses` | 🟢 | 🔴 |
463+
| `stx_getAccounts` | 🔴 | 🟢 |
464+
| `stx_getNetworks` | 🔴 | 🔴 |
465+
| `stx_transferStx` | 🟢 | 🟢 |
466+
| `stx_transferSip10Ft` | 🟢 | 🔴 |
467+
| `stx_transferSip9Nft` | 🟢 | 🔴 |
468+
| `stx_callContract` | 🟡 <sub>Hex-encoded Clarity values only</sub> | 🟡 <sub>Hex-encoded Clarity values only, no support for `postConditions`</sub> |
469+
| `stx_deployContract` | 🟡 <sub>Hex-encoded Clarity values only</sub> | 🟡 <sub>Hex-encoded Clarity values only, no support for `postConditions`</sub> |
470+
| `stx_signTransaction` | 🟡 <sub>Hex-encoded Clarity values only</sub> | 🟡 <sub>Hex-encoded Clarity values only</sub> |
471+
| `stx_signMessage` | 🟡 <sub>Hex-encoded Clarity values only</sub> | 🟡 <sub>Hex-encoded Clarity values only</sub> |
472+
| `stx_signStructuredMessage` | 🟡 <sub>Hex-encoded Clarity values only</sub> | 🟡 <sub>Hex-encoded Clarity values only</sub> |
473+
| `stx_updateProfile` | 🔴 | 🔴 |
474+
475+
| Event | Leather | Xverse |
476+
| --------------------- | ------- | ------ |
477+
| `accountChange` | 🔴 | 🟢 |
478+
| `accountDisconnected` | 🔴 | 🟢 |
479+
| `networkChange` | 🔴 | 🟢 |
480+
| `stx_accountChange` | 🔴 | 🔴 |
481+
| `stx_networkChange` | 🔴 | 🔴 |
482+
483+
- 🔴 No support (yet)
484+
- 🟡 Partial support
485+
- 🟢 Supported
486+
379487
---
380488

381489
<div align="center"><br>

packages/connect/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './auth';
22
export * from './providers';
33
export * from './types';
44
export * from './ui';
5+
export * from './errors';
56

67
// Manual exports to avoid exporting internals (e.g. `LEGACY_XYZ`)
78
export { getDefaultPsbtRequestOptions, makePsbtToken, openPsbtRequestPopup } from './bitcoin';

packages/connect/src/request.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ export interface ConnectRequestOptions {
3232
*/
3333
provider?: StacksProvider;
3434

35-
/**
36-
* The default wallets to display in the modal.
37-
* Defaults to some known popular wallets.
38-
*/
39-
defaultProviders?: WbipProvider[];
40-
4135
/**
4236
* Forces the user to select a wallet.
4337
* Defaults to `false`.
@@ -62,6 +56,18 @@ export interface ConnectRequestOptions {
6256
*/
6357
enableLocalStorage?: boolean;
6458

59+
/**
60+
* The default wallets to display in the modal.
61+
* Defaults to some known popular wallets.
62+
*/
63+
defaultProviders?: WbipProvider[];
64+
65+
/**
66+
* A list of provider IDs that are approved to be shown in the Stacks Connect modal.
67+
* If not provided, all default and installed providers will be shown.
68+
*/
69+
approvedProviderIds?: string[];
70+
6571
// todo: maybe add callbacks, if set use them instead of throwing errors
6672
}
6773

@@ -196,8 +202,11 @@ export async function request<M extends keyof Methods>(
196202

197203
return new Promise((resolve, reject) => {
198204
const element = document.createElement('connect-modal');
199-
element.defaultProviders = opts.defaultProviders;
200-
element.installedProviders = getInstalledProviders(opts.defaultProviders);
205+
element.defaultProviders = filterProviders(opts.approvedProviderIds, opts.defaultProviders);
206+
element.installedProviders = filterProviders(
207+
opts.approvedProviderIds,
208+
getInstalledProviders(opts.defaultProviders)
209+
);
201210

202211
const originalOverflow = document.body.style.overflow;
203212
document.body.style.overflow = 'hidden';
@@ -254,6 +263,12 @@ function requestArgs<M extends keyof Methods>(
254263
return { options: args[0], method: args[1] as M, params: args[2] };
255264
}
256265

266+
/** @internal */
267+
function filterProviders(approvedProviderIds: string[], providers: WbipProvider[]): WbipProvider[] {
268+
if (!approvedProviderIds) return providers;
269+
return providers.filter(p => approvedProviderIds.includes(p.id));
270+
}
271+
257272
/**
258273
* Initiate a wallet connection and request addresses.
259274
* Alias for `request` to `getAddresses` with `forceWalletSelect: true`.

packages/connect/src/stories/Connect.stories.tsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import React from 'react';
12
import type { Meta, StoryObj } from '@storybook/react';
23
import { DEFAULT_PROVIDERS } from '../providers';
34
import { ConnectPage } from './ConnectPage';
45
import { WebBTCProvider } from '@stacks/connect-ui';
6+
import { connect } from '../request';
57

68
// Define window types
79
declare global {
@@ -59,3 +61,78 @@ export const WithMockedWallet: Story = {
5961
};
6062
},
6163
};
64+
65+
// 4. With approved providers only
66+
export const WithApprovedProvidersOnly: Story = {
67+
render: () => {
68+
const handleConnect = () => {
69+
return connect({
70+
approvedProviderIds: ['LeatherProvider'],
71+
});
72+
};
73+
74+
return (
75+
<ConnectPage customConnectFunction={handleConnect}>
76+
<p>This demo only allows Leather wallet. Click connect() to test.</p>
77+
</ConnectPage>
78+
);
79+
},
80+
};
81+
82+
// 5. With multiple approved providers
83+
export const WithMultipleApprovedProviders: Story = {
84+
render: () => {
85+
const handleConnect = () => {
86+
return connect({
87+
approvedProviderIds: ['LeatherProvider', 'FordefiProviders.UtxoProvider'],
88+
});
89+
};
90+
91+
return (
92+
<ConnectPage customConnectFunction={handleConnect}>
93+
<p>This demo allows only Leather and Fordefi wallets. Click connect() to test.</p>
94+
</ConnectPage>
95+
);
96+
},
97+
};
98+
99+
// 6. With approved providers and custom default providers
100+
export const WithApprovedAndCustomProviders: Story = {
101+
render: () => {
102+
const handleConnect = () => {
103+
// Create a custom set of providers
104+
const customProviders = [
105+
{
106+
id: 'custom-wallet-1',
107+
name: 'Custom Wallet 1',
108+
icon: 'https://via.placeholder.com/48',
109+
},
110+
{
111+
id: 'leather',
112+
name: 'Custom Leather',
113+
icon: 'https://www.leather.io/favicon.ico',
114+
},
115+
{
116+
id: 'custom-wallet-2',
117+
name: 'Custom Wallet 2',
118+
icon: 'https://via.placeholder.com/48',
119+
},
120+
];
121+
122+
return connect({
123+
approvedProviderIds: ['leather', 'xverse'],
124+
defaultProviders: customProviders,
125+
forceWalletSelect: true,
126+
});
127+
};
128+
129+
return (
130+
<ConnectPage customConnectFunction={handleConnect}>
131+
<p>
132+
This demo combines approvedProviderIds with custom defaultProviders. Only "Custom Leather"
133+
should appear.
134+
</p>
135+
</ConnectPage>
136+
);
137+
},
138+
};

0 commit comments

Comments
 (0)