Skip to content

Commit

Permalink
fix: add params to connect method (#429)
Browse files Browse the repository at this point in the history
* fix: update connect alias

* fix: update xverse address storage

* chore: add changesets

* fix: update options check

* test: update demo app for local storage

* fix: add authResponsePayload again for backwards compatibility

* fix: strip public key and derivation path

* docs: add connect react note

---------

Co-authored-by: janniks <[email protected]>
  • Loading branch information
janniks and janniks authored Feb 27, 2025
1 parent 44d43c3 commit cf1fcb4
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-colts-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect': patch
---

Add authResponsePayload to authentication result for more backwards compatibility
5 changes: 5 additions & 0 deletions .changeset/good-cheetahs-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect': patch
---

Allow `connect()` to take any `getAddresses` params as options
5 changes: 5 additions & 0 deletions .changeset/great-pugs-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect': patch
---

Fix address storage issue for Xverse-like wallets
5 changes: 5 additions & 0 deletions .changeset/little-toys-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect': patch
---

Remove potentially unwanted private information from being stored in local storage
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ Follow the **Getting Started** section of the [`@stacks/connect` README](https:/
This repository includes three packages:

- [`@stacks/connect`](./packages/connect): The one-stop-shop tool for letting web-apps interact with Stacks web wallets.
- [`@stacks/connect-react`](./packages/connect-react): A wrapper library for making `@stacks/connect` use in React even easier
- [`@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`)_.
- ~~[`@stacks/connect-react`](./packages/connect-react): A wrapper library for making `@stacks/connect` use in React even easier~~

## 🛠️ Wallet Implementation Guide

Expand Down
6 changes: 2 additions & 4 deletions packages/connect-react/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# `@stacks/connect`
# `@stacks/connect-react`

A library for building excellent user experiences with [Blockstack](https://blockstack.org/).

:blue_book: [View documentation](https://docs.blockstack.org/develop/connect/overview.html)
A new `@stacks/react` experience is coming soon 🤫
2 changes: 1 addition & 1 deletion packages/connect/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const authenticate = async (authOptions: AuthOptions, provider?: StacksPr

userSession.store.setSessionData(sessionData);

onFinish?.({ userSession });
onFinish?.({ userSession, authResponsePayload: sessionData.userData });
} catch (error) {
console.error('[Connect] Error during auth request', error);
onCancel?.(error);
Expand Down
2 changes: 2 additions & 0 deletions packages/connect/src/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export interface SignStructuredMessageParams {

export interface GetAddressesParams {
network?: NetworkString;
// When updating this interface, make sure to update the `connect` function
// in `request.ts`, to pass all available params to the wrapped method.
}

export interface SendTransferParams {
Expand Down
26 changes: 22 additions & 4 deletions packages/connect/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { defineCustomElements } from '@stacks/connect-ui/loader';
import { Cl, PostCondition, postConditionToHex } from '@stacks/transactions';
import { JsonRpcError, JsonRpcErrorCode } from './errors';
import {
AddressEntry,
MethodParams,
MethodParamsRaw,
MethodResult,
Expand Down Expand Up @@ -95,8 +96,11 @@ function createRequestWithStorage(enableLocalStorage: boolean): typeof requestRa
params?: MethodParamsRaw<M>
): Promise<MethodResultRaw<M>> {
const result = await requestRaw(provider, method, params);
if (method === 'getAddresses' && 'addresses' in result) {
const { stx, btc } = result.addresses.reduce(
if (
(method === 'getAddresses' || (method as string) === 'wallet_connect') &&
'addresses' in result
) {
const { stx, btc } = sortAddresses(result.addresses).reduce(
(acc, addr) => {
acc[addr.address.startsWith('S') ? 'stx' : 'btc'].push(addr);
return acc;
Expand Down Expand Up @@ -251,8 +255,9 @@ function requestArgs<M extends keyof Methods>(
* Initiate a wallet connection and request addresses.
* Alias for `request` to `getAddresses` with `forceWalletSelect: true`.
*/
export function connect(options?: ConnectRequestOptions) {
return request({ ...options, forceWalletSelect: true }, 'getAddresses');
export function connect(options?: ConnectRequestOptions & MethodParams<'getAddresses'>) {
const params = options && 'network' in options ? { network: options.network } : undefined;
return request({ ...options, forceWalletSelect: true }, 'getAddresses', params);
}

/**
Expand Down Expand Up @@ -478,3 +483,16 @@ function createPersistProviderProxy(shouldPersist: boolean, providerId: string)
return result;
};
}

/** @internal */
function sortAddresses(addresses: AddressEntry[]) {
return addresses.slice().sort((a, b) => {
const aPayment = 'purpose' in a && a.purpose === 'payment';
const bPayment = 'purpose' in b && b.purpose === 'payment';

// Move payment addresses to the beginning
if (aPayment && !bPayment) return -1;
if (!aPayment && bPayment) return 1;
return 0;
});
}
7 changes: 6 additions & 1 deletion packages/connect/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ export const normalizeAddresses = (
addresses: AddressEntry[]
): Omit<AddressEntry, 'publicKey'>[] => {
const deduped = [...new Map(addresses.map(a => [a.address, a])).values()];
return deduped.map(({ publicKey, ...rest }) => rest);
return deduped.map(({ ...a }) => {
if ('publicKey' in a) delete a.publicKey;
if ('derivationPath' in a) delete a.derivationPath;
if ('tweakedPublicKey' in a) delete a.tweakedPublicKey;
return a;
});
};

/**
Expand Down
28 changes: 24 additions & 4 deletions packages/connect/src/stories/ConnectPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { getSelectedProviderId } from '@stacks/connect-ui';
import { Cl } from '@stacks/transactions';
import { useReducer, useState } from 'react';
import { useReducer, useState, useEffect } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { AppConfig, UserSession } from '../auth';
import { connect, request } from '../request';
Expand All @@ -15,7 +15,7 @@ import {
showSTXTransfer,
} from '../ui';
import './connect.css';
import { disconnect, isConnected } from '../storage';
import { disconnect, isConnected, getLocalStorage } from '../storage';

declare global {
interface BigInt {
Expand Down Expand Up @@ -1025,6 +1025,11 @@ export const ConnectPage = ({ children }: { children?: any }) => {
const refresh = useReducer(x => x + 1, 0)[1];
const isSignedIn = userSession.isUserSignedIn();
const [connectResponse, setConnectResponse] = useState<any>(null);
const [localStorageData, setLocalStorageData] = useState<any>(null);

useEffect(() => {
setLocalStorageData(getLocalStorage());
}, [refresh]);

const appDetails = {
name: 'Connect',
Expand All @@ -1041,12 +1046,16 @@ export const ConnectPage = ({ children }: { children?: any }) => {
if (isSignedIn) {
disconnect();
setConnectResponse(null);
setLocalStorageData(null);
refresh();
} else {
void showConnect({
appDetails,
userSession,
onFinish: d => {
setConnectResponse(d);
// Explicitly update localStorage data after successful connection
setLocalStorageData(getLocalStorage());
refresh();
},
onCancel: () => {
Expand All @@ -1055,7 +1064,6 @@ export const ConnectPage = ({ children }: { children?: any }) => {
},
});
}
refresh();
}}
style={{
backgroundColor: isSignedIn ? 'grey' : 'black',
Expand All @@ -1070,11 +1078,19 @@ export const ConnectPage = ({ children }: { children?: any }) => {
if (isConnected()) {
disconnect();
setConnectResponse(null);
// Explicitly clear localStorage data display after disconnect
setLocalStorageData(null);
refresh();
return;
}

void connect().then(setConnectResponse).then(refresh);
void connect()
.then(setConnectResponse)
.then(() => {
// Explicitly update the localStorage data after successful connection
setLocalStorageData(getLocalStorage());
refresh();
});
}}
style={{
backgroundColor: isConnected() ? 'grey' : 'black',
Expand All @@ -1097,6 +1113,10 @@ export const ConnectPage = ({ children }: { children?: any }) => {
<pre>{JSON.stringify(connectResponse, null, 2)}</pre>
</div>
)}
<div data-response>
<h3>LocalStorage Content</h3>
<pre>{JSON.stringify(localStorageData, null, 2)}</pre>
</div>
</section>

{(isSignedIn || isConnected()) && (
Expand Down
43 changes: 29 additions & 14 deletions packages/connect/src/types/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,36 @@ import { UserSession } from '../auth';

/** @deprecated */
export interface AuthResponsePayload {
private_key: string;
username: string | null;
hubUrl: string;
associationToken: string;
blockstackAPIUrl: string | null;
core_token: string | null;
email: string | null;
exp: number;
iat: number;
iss: string;
jti: string;
version: string;
profile: any;
profile_url: string;
public_keys: string[];

/** @deprecated Not set in the `request` flow anymore. */
private_key?: string;
/** @deprecated Not set in the `request` flow anymore. */
username?: string | null;
/** @deprecated Not set in the `request` flow anymore. */
hubUrl?: string;
/** @deprecated Not set in the `request` flow anymore. */
associationToken?: string;
/** @deprecated Not set in the `request` flow anymore. */
blockstackAPIUrl?: string | null;
/** @deprecated Not set in the `request` flow anymore. */
core_token?: string | null;
/** @deprecated Not set in the `request` flow anymore. */
email?: string | null;
/** @deprecated Not set in the `request` flow anymore. */
exp?: number;
/** @deprecated Not set in the `request` flow anymore. */
iat?: number;
/** @deprecated Not set in the `request` flow anymore. */
iss?: string;
/** @deprecated Not set in the `request` flow anymore. */
jti?: string;
/** @deprecated Not set in the `request` flow anymore. */
version?: string;
/** @deprecated Not set in the `request` flow anymore. */
profile_url?: string;
/** @deprecated Not set in the `request` flow anymore. */
public_keys?: string[];
}

/** @deprecated */
Expand Down

0 comments on commit cf1fcb4

Please sign in to comment.