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

feat: add profile update #280

Merged
merged 14 commits into from
Dec 7, 2022
5 changes: 5 additions & 0 deletions .changeset/eleven-readers-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect-react': minor
---

Adds `doProfileUpdate` method to allow users to update profiles via wallets
5 changes: 5 additions & 0 deletions .changeset/nervous-crabs-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@stacks/connect': minor
---

Adds `openProfileUpdateRequestPopup` method to allow users to update profiles via wallets
5 changes: 0 additions & 5 deletions .changeset/orange-camels-shave.md

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Create Release Pull Request
uses: changesets/action@640c4a5d646991720131ef3b6e0e63220de5dddf
uses: changesets/action@595655c3eae7136ff5ba18200406898904362926
with:
title: "chore: version packages"
commit: "chore: version packages"
Expand Down
33 changes: 22 additions & 11 deletions packages/connect-react/src/react/hooks/use-connect.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { useContext } from 'react';
import {
authenticate,
AuthOptions,
ContractCallOptions,
ContractCallRegularOptions,
ContractCallSponsoredOptions,
ContractDeployOptions,
STXTransferOptions,
ContractDeployRegularOptions,
ContractDeploySponsoredOptions,
FinishedAuthData,
openContractCall,
openContractDeploy,
openProfileUpdateRequestPopup,
openSignatureRequestPopup,
openStructuredDataSignatureRequestPopup,
openSTXTransfer,
ProfileUpdateRequestOptions,
showBlockstackConnect,
ContractCallRegularOptions,
ContractCallSponsoredOptions,
ContractDeployRegularOptions,
ContractDeploySponsoredOptions,
SignatureRequestOptions,
STXTransferOptions,
STXTransferRegularOptions,
STXTransferSponsoredOptions,
FinishedAuthData,
openStructuredDataSignatureRequestPopup,
openSignatureRequestPopup,
} from '@stacks/connect';
import { ConnectContext, ConnectDispatchContext, States } from '../components/connect/context';
import { SignatureRequestOptions } from '@stacks/connect';
import { StructuredDataSignatureRequestOptions } from '@stacks/connect/src/types/structuredDataSignature';
import { useContext } from 'react';
import { ConnectContext, ConnectDispatchContext, States } from '../components/connect/context';

const useConnectDispatch = () => {
const dispatch = useContext(ConnectDispatchContext);
Expand Down Expand Up @@ -110,6 +112,14 @@ export const useConnect = () => {
});
}

function doProfileUpdate(options: ProfileUpdateRequestOptions) {
return openProfileUpdateRequestPopup({
...options,
authOrigin: authOptions.authOrigin,
appDetails: authOptions.appDetails,
});
}

function sign(options: SignatureRequestOptions) {
return openSignatureRequestPopup({
...options,
Expand Down Expand Up @@ -138,6 +148,7 @@ export const useConnect = () => {
doContractCall,
doContractDeploy,
doSTXTransfer,
doProfileUpdate,
sign,
signStructuredData,
};
Expand Down
7 changes: 4 additions & 3 deletions packages/connect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
"typings": "dist/types/index.d.ts",
"unpkg": "dist/bundle.js",
"dependencies": {
"@stacks/auth": "^5.0.0",
"@stacks/auth": "^6.0.0",
"@stacks/connect-ui": "6.0.0",
"@stacks/network": "^5.0.0",
"@stacks/network": "^6.0.0",
"@stacks/prettier-config": "0.0.8",
"@stacks/transactions": "^5.0.0",
"@stacks/profile": "^6.0.0",
"@stacks/transactions": "^6.0.0",
"jsontokens": "^4.0.1",
"readable-stream": "^3.6.0",
"url": "^0.11.0"
Expand Down
1 change: 1 addition & 0 deletions packages/connect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './auth';
export * from './transactions';
export * from './signature';
export * from './signature/structuredData';
export * from './profile';
export * from './types';
export * from './utils';
export * from './ui';
Expand Down
76 changes: 76 additions & 0 deletions packages/connect/src/profile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { StacksTestnet } from '@stacks/network';
import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
import {
ProfileUpdatePayload,
ProfileUpdatePopup,
ProfileUpdateRequestOptions,
} from 'src/types/profile';
import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';

import { getStacksProvider } from '../utils';

async function signPayload(payload: ProfileUpdatePayload, privateKey: string) {
const tokenSigner = new TokenSigner('ES256k', privateKey);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return tokenSigner.signAsync({ ...payload } as any);
}

export function getDefaultProfileUpdateRequestOptions(options: ProfileUpdateRequestOptions) {
const network = options.network || new StacksTestnet();
const userSession = getUserSession(options.userSession);
const defaults: ProfileUpdateRequestOptions = {
...options,
network,
userSession,
};
return {
...defaults,
};
}

async function openProfileUpdatePopup({ token, options }: ProfileUpdatePopup) {
const provider = getStacksProvider();
if (!provider) {
throw new Error('Hiro Wallet not installed.');
}

try {
const profileUpdateResponse = await provider.profileUpdateRequest(token);
options.onFinish?.(profileUpdateResponse);
} catch (error) {
console.error('[Connect] Error during signature request', error);
options.onCancel?.();
}
}

export const makeProfileUpdateToken = async (options: ProfileUpdateRequestOptions) => {
const { userSession, profile, ..._options } = options;
if (hasAppPrivateKey(userSession)) {
const { privateKey, publicKey } = getKeys(userSession);

const payload: ProfileUpdatePayload = {
..._options,
profile,
publicKey,
};

return signPayload(payload, privateKey);
}
const payload = { ..._options };
return createUnsecuredToken(payload as Json);
};

async function generateTokenAndOpenPopup<T extends ProfileUpdateRequestOptions>(
options: T,
makeTokenFn: (options: T) => Promise<string>
) {
const token = await makeTokenFn({
...getDefaultProfileUpdateRequestOptions(options),
...options,
} as T);
return openProfileUpdatePopup({ token, options });
}

export function openProfileUpdateRequestPopup(options: ProfileUpdateRequestOptions) {
return generateTokenAndOpenPopup(options, makeProfileUpdateToken);
}
1 change: 1 addition & 0 deletions packages/connect/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './auth';
export * from './transactions';
export * from './signature';
export * from './structuredDataSignature';
export * from './profile';
39 changes: 39 additions & 0 deletions packages/connect/src/types/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { UserSession } from '@stacks/auth';
import { StacksNetwork } from '@stacks/network';
import { AuthOptions } from './auth';
import { PublicPersonProfile } from '@stacks/profile';

export type ProfileUpdateFinished = (data: PublicPersonProfile) => void;
export type ProfileUpdateCanceled = () => void;

export interface ProfileUpdateBase {
appDetails?: AuthOptions['appDetails'];
authOrigin?: string;
network?: StacksNetwork;
stxAddress?: string;
userSession?: UserSession;
onFinish?: ProfileUpdateFinished;
onCancel?: ProfileUpdateCanceled;
}

export interface CommonProfileUpdatePayload extends ProfileUpdateBase {
publicKey: string;
}

export interface ProfileUpdatePayload extends CommonProfileUpdatePayload {
profile: PublicPersonProfile;
}

// same as ProfileUpdatePayload without publicKey
export interface ProfileUpdateRequestOptions extends ProfileUpdateBase {
profile: PublicPersonProfile;
}

/**
* Transaction Popup
*/

export interface ProfileUpdatePopup {
token: string;
options: ProfileUpdateRequestOptions;
}
2 changes: 2 additions & 0 deletions packages/connect/src/types/provider.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PublicProfile } from '@stacks/profile';
import { SignatureData } from './signature';
import { FinishedTxPayload, SponsoredFinishedTxPayload } from './transactions';

Expand All @@ -20,6 +21,7 @@ export interface StacksProvider {
authenticationRequest(payload: string): Promise<string>;
signatureRequest(payload: string): Promise<SignatureData>;
structuredDataSignatureRequest(payload: string): Promise<SignatureData>;
profileUpdateRequest(payload: string): Promise<PublicProfile>;
request(method: string, params?: any[]): Promise<Record<string, any>>;
getProductInfo:
| undefined
Expand Down
88 changes: 44 additions & 44 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2388,15 +2388,15 @@
call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0"

"@noble/hashes@^1.0.0", "@noble/hashes@^1.1.2", "@noble/hashes@~1.1.1":
"@noble/hashes@^1.1.2", "@noble/hashes@~1.1.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==

"@noble/secp256k1@^1.5.5":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.0.tgz#602afbbfcfb7e169210469b697365ef740d7e930"
integrity sha512-DWSsg8zMHOYMYBqIQi96BQuthZrp98LCeMNcUOaffCIVYQ5yxDbNikLF+H7jEnmNNmXbtVic46iCuVWzar+MgA==
"@noble/hashes@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.3.tgz#360afc77610e0a61f3417e497dcf36862e4f8111"
integrity sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A==

"@noble/secp256k1@^1.6.3":
version "1.6.3"
Expand Down Expand Up @@ -2631,36 +2631,36 @@
dependencies:
type-detect "4.0.8"

"@stacks/auth@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/auth/-/auth-5.0.0.tgz#3df032b906cf252fe498b0b8ca5a96346a40fed6"
integrity sha512-etjiBpp65HUeAUIGXF6JInsX+eb7uWI6IPAGpffuxd/AfIxB+wguJBltAeYYFItWjuvvsevOMKMFCNWwD+xXrA==
"@stacks/auth@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/auth/-/auth-6.0.0.tgz#615927e9f6c0c122a1e4a2245ec4e26721a591a4"
integrity sha512-2BdNl52vgIvfM/MHGsSt6b+1XgbxV1OryNCeLf+bI4myabYXuQXQUCQskkLj6w4PexC2AX5gMt6Q4kXrUP+Mzw==
dependencies:
"@stacks/common" "^5.0.0"
"@stacks/encryption" "^5.0.0"
"@stacks/network" "^5.0.0"
"@stacks/profile" "^5.0.0"
"@stacks/common" "^6.0.0"
"@stacks/encryption" "^6.0.0"
"@stacks/network" "^6.0.0"
"@stacks/profile" "^6.0.0"
cross-fetch "^3.1.5"
jsontokens "^4.0.1"
query-string "^6.13.1"

"@stacks/common@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/common/-/common-5.0.0.tgz#278f90e0cf8c05dd4f46bcebccd1f1ea93749906"
integrity sha512-YlgvCedA+W3MZhOTw3ZKOE66GhD34zpnmh0fbIeWEPM7TQXZ61U+28oUfMquvfTx3KNDE4dlCshoqFdDw1ZY8A==
"@stacks/common@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/common/-/common-6.0.0.tgz#ce80a11ca5d16db8276891db3ecc8e4f2eb1b43a"
integrity sha512-tETwccvbYvaZ7u3ZucWNMOIPN97r6IPeZXKIFhLc1KSVaWSGEPTtZcwVp+Rz3mu2XgI2pg37SUrOWXSL7OOkDw==
dependencies:
"@types/bn.js" "^5.1.0"
"@types/node" "^18.0.4"

"@stacks/encryption@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/encryption/-/encryption-5.0.0.tgz#c373075f2b7fc7aa94a1e1e10ea16c38e6744f39"
integrity sha512-aUe/BEe9zhHi95xDGXD9bxdaiP9MmIiQMo4NPq8drlZm46ap2g4SP3kMdeCxpk2KTEmAGfsRCy78jQ7cKuJujQ==
"@stacks/encryption@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/encryption/-/encryption-6.0.0.tgz#3b1e8fbd113d77eb6cff42900568bf94f2b20f9d"
integrity sha512-Wcp3eOtW2PYRjFY5GRFMNmD6r18prB5xiBc8HODU7hUSHTicMhQxGh0x5M+y3WA9iSTSeSVF/uOS8KFZYg0RYg==
dependencies:
"@noble/hashes" "^1.0.0"
"@noble/secp256k1" "^1.5.5"
"@noble/hashes" "^1.1.3"
"@noble/secp256k1" "^1.6.3"
"@scure/bip39" "^1.1.0"
"@stacks/common" "^5.0.0"
"@stacks/common" "^6.0.0"
"@types/node" "^18.0.4"
base64-js "^1.5.1"
bs58 "^5.0.0"
Expand All @@ -2681,12 +2681,12 @@
eslint-plugin-import ">=2.23.3"
eslint-plugin-prettier "^3.4.0"

"@stacks/network@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/network/-/network-5.0.0.tgz#fd70fd605d942cfe2ce5c037140b4f1698313e61"
integrity sha512-fv5eCuWv+NkuhgubDvzymoEOBAEfxq/AGLF25ghMCA7dYfsb80iRKr5dwKgUIFCUWcqmzLBJUVmzxFnXkblF8g==
"@stacks/network@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/network/-/network-6.0.0.tgz#c6bc59e7861c51e1e580782132a380ca550ba0f3"
integrity sha512-7acFYeR7YEmS8ZvK21OmI31x2jsjf1lepXV5qMs34DMz4bRylj5ZZ650lavvXk2a5o8/lop2qWQtvo9PtZ/ESQ==
dependencies:
"@stacks/common" "^5.0.0"
"@stacks/common" "^6.0.0"
cross-fetch "^3.1.5"

"@stacks/prettier-config@0.0.8", "@stacks/prettier-config@^0.0.8":
Expand All @@ -2701,27 +2701,27 @@
resolved "https://registry.yarnpkg.com/@stacks/prettier-config/-/prettier-config-0.0.7.tgz#22525c623fce6a5f1965fd29867df3d791ea7ef5"
integrity sha512-fXd5X4SV5GqskCYZxqExZJ7wF5sOggmzFP4Q7ttmR/eXZKar3c9fqEpbrpRZyHUBZv2Nss46MvwTLQk2YlpIeg==

"@stacks/profile@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/profile/-/profile-5.0.0.tgz#a73efa0da9d25075cdcf7700982481203522f18f"
integrity sha512-/SxXtKJLsoLeOdBeytmtnfCKQ6jfzjdRW9DIvd2hq8fXCI3mItTXSLMUna0mabVH5m166P1Ko6oZl4vu0POMJg==
"@stacks/profile@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/profile/-/profile-6.0.0.tgz#e50a809eef3fca66cf0745d065b836862648adf4"
integrity sha512-XzawNHUh2vq8kXxrKgfoWzbcNziYmiNy+qWCWwFk39N/dAjqIGSnZrc2eomrHHbWh2lkBXxxQggCGpoZJLhvMw==
dependencies:
"@stacks/common" "^5.0.0"
"@stacks/network" "^5.0.0"
"@stacks/transactions" "^5.0.0"
"@stacks/common" "^6.0.0"
"@stacks/network" "^6.0.0"
"@stacks/transactions" "^6.0.0"
jsontokens "^4.0.1"
schema-inspector "2.0.1"
zone-file "^2.0.0-beta.3"

"@stacks/transactions@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@stacks/transactions/-/transactions-5.0.0.tgz#51e6aa3d75cb49f36448004588b4ad9f3d84832f"
integrity sha512-OqmN7FRKSt1pHKYmNn4jbBoms7CJHlcQd2ZoQld5eVF3whWsA9Nv2iUfsNH+vppZIpG6TLpY752LyEDz4aiCsQ==
"@stacks/transactions@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@stacks/transactions/-/transactions-6.0.0.tgz#bbc817e61bd2fc116fc276f85d1387644f8a8f56"
integrity sha512-TQc6yhTMUhoCrN5gZ+YqzMHQ4VnL7pWhfYP96vs6mNyRchiYWhU+zONbKQ0KoVeYl3GUiOUh/9WsL6qeTmK6qQ==
dependencies:
"@noble/hashes" "^1.0.0"
"@noble/secp256k1" "^1.5.5"
"@stacks/common" "^5.0.0"
"@stacks/network" "^5.0.0"
"@noble/hashes" "^1.1.3"
"@noble/secp256k1" "^1.6.3"
"@stacks/common" "^6.0.0"
"@stacks/network" "^6.0.0"
c32check "^2.0.0"
lodash.clonedeep "^4.5.0"

Expand Down