diff --git a/.changeset/bright-pumpkins-speak.md b/.changeset/bright-pumpkins-speak.md
new file mode 100644
index 00000000..73102fa5
--- /dev/null
+++ b/.changeset/bright-pumpkins-speak.md
@@ -0,0 +1,17 @@
+---
+'@stacks/connect-react': major
+'@stacks/connect': major
+---
+
+Switch to new RPC API with SIP-030
+
+BREAKING CHANGE:
+
+Adds the new `request` method to the `@stacks/connect` package.
+Which is the default way of interacting with Bitcoin and Stacks wallets.
+This update needs a slightly breaking change to the `showXyz` and `openXyz` methods.
+
+- ADDED `request` (UI) and `requestRaw` for calling wallet RPC methods
+- UPDATED `SessionData` and `UserSession` to only expose a light `UserData` with `profile.stxAddress`
+- UPDATED `StacksProvider` to only have a `request` method
+- REMOVED `BlockstackProvider`, `StacksProvider`
diff --git a/.changeset/lovely-jobs-fail.md b/.changeset/lovely-jobs-fail.md
new file mode 100644
index 00000000..e84cd6d3
--- /dev/null
+++ b/.changeset/lovely-jobs-fail.md
@@ -0,0 +1,10 @@
+---
+'@stacks/connect-ui': major
+---
+
+Update UI properties
+
+BREAKING CHANGE:
+
+- RENAMED `persistSelection` to `persistWalletSelect`
+- REMOVED `shouldUsePopup` from `@stacks/connect-ui`
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index e1c8a43a..0e89ae5f 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -67,11 +67,14 @@ jobs:
- name: Get git commit
id: git-commit
run: echo "::set-output name=sha::$(git rev-parse --short HEAD)"
- - name: print preid
- env:
- BRANCH: ${{ steps.git-branch.outputs.branch }}
- SHA: ${{ steps.git-commit.outputs.sha }}
- run: echo $BRANCH.$SHA
+ - name: Set preid
+ id: preid
+ run: |
+ if [ "${{ steps.git-branch.outputs.branch }}" = "beta" ]; then
+ echo "preid=beta" >> "$GITHUB_OUTPUT"
+ else
+ echo "preid=alpha.${{ steps.git-commit.outputs.sha }}" >> "$GITHUB_OUTPUT"
+ fi
- name: Setup git
run: |
git config --local user.email "action@github.com"
@@ -79,15 +82,15 @@ jobs:
- name: Publish to NPM
env:
BRANCH: ${{ steps.git-branch.outputs.branch }}
- SHA: ${{ steps.git-commit.outputs.sha }}
- run: npx lerna publish prepatch --preid alpha.$SHA --dist-tag $BRANCH --yes --no-push
- - name: Get alpha package versions
- id: alpha
+ PREID: ${{ steps.preid.outputs.preid }}
+ run: npx lerna publish prepatch --preid $PREID --dist-tag $BRANCH --yes --no-push
+ - name: Get package versions
+ id: versions
run: |
echo "connect=$(cat packages/connect/package.json | jq -r '.version')" >> "$GITHUB_OUTPUT"
echo "connectreact=$(cat packages/connect-react/package.json | jq -r '.version')" >> "$GITHUB_OUTPUT"
echo "connectui=$(cat packages/connect-ui/package.json | jq -r '.version')" >> "$GITHUB_OUTPUT"
- uses: janniks/pull-request-fixed-header@v1.0.1
with:
- header: "> This PR was published to npm with the alpha versions:\n> - connect `npm install @stacks/connect@${{ steps.alpha.outputs.connect }} --save-exact`\n> - connect-react `npm install @stacks/connect-react@${{ steps.alpha.outputs.connectreact }} --save-exact`\n> - connect-ui `npm install @stacks/connect-ui@${{ steps.alpha.outputs.connectui }} --save-exact`"
+ header: "> This PR was published to npm with versions:\n> - connect `npm install @stacks/connect@${{ steps.versions.outputs.connect }} --save-exact`\n> - connect-react `npm install @stacks/connect-react@${{ steps.versions.outputs.connectreact }} --save-exact`\n> - connect-ui `npm install @stacks/connect-ui@${{ steps.versions.outputs.connectui }} --save-exact`"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index 91de42a4..48d1562d 100644
--- a/README.md
+++ b/README.md
@@ -20,25 +20,75 @@ This repository includes three packages:
- [`@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`)_.
-## 🌎 More Information
-
-The [Stacks documentation website](https://docs.stacks.co/build-apps/overview) includes more examples for building apps using Connect.
-
-It also includes guides for various aspects of Stacks application development:
-
-- [Authentication](https://docs.stacks.co/build-apps/references/authentication)
-- [Transactions](https://docs.stacks.co/understand-stacks/technical-specs#transactions)
-- [Data storage](https://docs.stacks.co/build-apps/references/gaia#understand-data-storage)
-
-## 🐛 Bugs and feature requests
-
-If you encounter a bug or have a feature request, we encourage you to follow the steps below:
-
-1. **Search for existing issues:** Before submitting a new issue, please search [existing and closed issues](https://github.com/hirosystems/connect/issues) to check if a similar problem or feature request has already been reported.
-1. **Open a new issue:** If it hasn't been addressed, please [open a new issue](https://github.com/hirosystems/connect/issues/new/choose). Choose the appropriate issue template and provide as much detail as possible, including steps to reproduce the bug or a clear description of the requested feature.
-1. **Evaluation SLA:** Our team reads and evaluates all the issues and pull requests. We are avaliable Monday to Friday and we make a best effort to respond within 7 business days.
-
-Please **do not** use the issue tracker for personal support requests or to ask for the status of a transaction. You'll find help at the [#stacks-js Discord channel](https://stacks.chat/).
+## 🛠️ Wallet Implementation Guide
+
+Wallets implement a "Provider" interface.
+The latest spec uses a simple JS Object exposing a `.request(method: string, params?: object)` method.
+
+Pseudo-code:
+
+```ts
+window.MyProvider = {
+ async request(method, params) {
+ // Somehow communicate with the wallet (e.g. via events)
+
+ // Recommendation: Create a JSON RPC 2.0 request object
+ // https://www.jsonrpc.org/specification
+
+ return Promise.resolve({
+ // Respond with a JSON RPC 2.0 response object
+ id: crypto.randomUUID(), // required, same as request
+ jsonrpc: '2.0', // required
+
+ // `.result` is required on success
+ result: {
+ // object matching specified RPC methods
+ },
+
+ // `.error` is required on error
+ error: {
+ // Use existing codes from https://www.jsonrpc.org/specification#error_object
+ code: number, // required, integer
+ message: string, // recommended, single sentence
+ data: object, // optional
+ },
+ });
+ },
+ isMyWallet: true, // optional, a way of identifying the wallet for developers
+};
+
+window.wbip_providers = window.wbip_providers || [];
+window.wbip_providers.push({
+ // `WbipProvider` type
+ /** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */
+ id: 'MyProvider',
+ /** The name of the provider, as displayed to the user */
+ name: 'My Wallet';
+ /** 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 */
+ icon?: 'data:image/png;base64,iVBORw0...';
+ /** Web URL of the provider */
+ webUrl?: 'https://mywallet.example.com';
+
+ // Addional URLs
+ chromeWebStoreUrl?: string;
+ mozillaAddOnsUrl?: string;
+ googlePlayStoreUrl?: string;
+ iOSAppStoreUrl?: string;
+});
+```
+
+### JSON RPC 2.0
+
+Wallets may add their own unstandardized methods.
+However, the minimum recommended methods are:
+
+- `getAddresses` [WBIP](https://wbips.netlify.app/request_api/getAddresses)
+- `signPsbt` [WBIP](https://wbips.netlify.app/request_api/signPsbt)
+- `stx_getAddresses` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_transferStx` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_callContract` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_signMessage` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_signStructuredMessage` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
## 🎁 Contribute
@@ -59,3 +109,14 @@ Join our community and stay connected with the latest updates and discussions:
- [Join our Discord community chat](https://stacks.chat/) to engage with other users, ask questions, and participate in discussions.
- [Visit hiro.so](https://www.hiro.so/) for updates and subcribing to the mailing list.
- Follow [Hiro on Twitter.](https://twitter.com/hirosystems)
+
+### MIGRATION TODO
+
+#### TODO
+
+- Add `PostConditionModeName` to all options (new and old) — This might have been missing since the v7 release.
+- Strip unserializable fields from RawLegacy wrapper just in case.
+- Remove exports from LEGACY_XYZ
+- Make hex to base64 for psbt
+
+Search for the below and replace with inline return.
diff --git a/package-lock.json b/package-lock.json
index 837055d7..449bfb02 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,9 @@
"workspaces": [
"packages/**"
],
+ "dependencies": {
+ "type-fest": "^4.34.1"
+ },
"devDependencies": {
"@babel/preset-typescript": "^7.26.0",
"@changesets/changelog-github": "^0.5.0",
@@ -27,7 +30,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-unused-imports": "^3.2.0",
"husky": "^9.1.6",
- "lerna": "8.1.8",
+ "lerna": "8.1.9",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"react": "^18.3.1",
@@ -3345,16 +3348,16 @@
}
},
"node_modules/@lerna/create": {
- "version": "8.1.8",
- "resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.8.tgz",
- "integrity": "sha512-wi72R01tgjBjzG2kjRyTHl4yCTKDfDMIXRyKz9E/FBa9SkFvUOAE4bdyY9MhEsRZmSWL7+CYE8Flv/HScRpBbA==",
+ "version": "8.1.9",
+ "resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.9.tgz",
+ "integrity": "sha512-DPnl5lPX4v49eVxEbJnAizrpMdMTBz1qykZrAbBul9rfgk531v8oAt+Pm6O/rpAleRombNM7FJb5rYGzBJatOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@npmcli/arborist": "7.5.4",
"@npmcli/package-json": "5.2.0",
"@npmcli/run-script": "8.1.0",
- "@nx/devkit": ">=17.1.2 < 20",
+ "@nx/devkit": ">=17.1.2 < 21",
"@octokit/plugin-enterprise-rest": "6.0.1",
"@octokit/rest": "19.0.11",
"aproba": "2.0.0",
@@ -3367,7 +3370,7 @@
"console-control-strings": "^1.1.0",
"conventional-changelog-core": "5.0.1",
"conventional-recommended-bump": "7.0.1",
- "cosmiconfig": "^8.2.0",
+ "cosmiconfig": "9.0.0",
"dedent": "1.5.3",
"execa": "5.0.0",
"fs-extra": "^11.2.0",
@@ -3393,7 +3396,7 @@
"npm-package-arg": "11.0.2",
"npm-packlist": "8.0.2",
"npm-registry-fetch": "^17.1.0",
- "nx": ">=17.1.2 < 20",
+ "nx": ">=17.1.2 < 21",
"p-map": "4.0.0",
"p-map-series": "2.1.0",
"p-queue": "6.6.2",
@@ -3498,37 +3501,10 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@lerna/create/node_modules/cosmiconfig": {
- "version": "8.3.6",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
- "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "import-fresh": "^3.3.0",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.2.0",
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- },
- "peerDependencies": {
- "typescript": ">=4.9.5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/@lerna/create/node_modules/fs-extra": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
- "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "version": "11.3.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
+ "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4301,38 +4277,13 @@
"node": "^16.13.0 || >=18.0.0"
}
},
- "node_modules/@nrwl/devkit": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.8.6.tgz",
- "integrity": "sha512-F6+4Lv2hSS+02H7aqa+jYIHzbmip7082DF9/NkNtUAEqLUi8STsbung0nchaR1Tjg20E+BZujEsZgTC3GJegLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nx/devkit": "19.8.6"
- }
- },
- "node_modules/@nrwl/tao": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.8.6.tgz",
- "integrity": "sha512-ibxGL7aDpNARgPegXQ8HAocemZ1WvZE5+NHkXDs7jSmnSt9qaXIKE1dXotDTqp3TqCirlje1/RMMTqzCl2oExQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "nx": "19.8.6",
- "tslib": "^2.3.0"
- },
- "bin": {
- "tao": "index.js"
- }
- },
"node_modules/@nx/devkit": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.8.6.tgz",
- "integrity": "sha512-8NAdnqwzki3srj2sAImWQ9cQiq79NqwqVqx/XOdg0XHR6siugn+sAAXWpM3xJVdv4uRbcyz7BO1GWYxMW0AOYA==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.4.2.tgz",
+ "integrity": "sha512-JD/7E/e49P7V9ESQK8b7uEzxgp1TP9Op163QmsJ6In0fpv3RytZSmAUx7lBdwOuOS6yybz8UWSLC/tyADUfDcg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nrwl/devkit": "19.8.6",
"ejs": "^3.1.7",
"enquirer": "~2.3.6",
"ignore": "^5.0.4",
@@ -4370,9 +4321,9 @@
}
},
"node_modules/@nx/nx-darwin-arm64": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.8.6.tgz",
- "integrity": "sha512-lzFV07gUgvy07lPtRFJFhlQdcR0qNTPPq7/ZB+3alwUIDdAn706ZVzf6apCJWOBIgNFKbAQiy/du0zmuKPSzXA==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.4.2.tgz",
+ "integrity": "sha512-djXV3rZcDdps2TUo7bMNiB6IkxFlLIZfub5cxPhxSbnrKiMGqmISZNn9n0AmchpNNL6auRWZPAPtDfowtR5GqA==",
"cpu": [
"arm64"
],
@@ -4387,9 +4338,9 @@
}
},
"node_modules/@nx/nx-darwin-x64": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.8.6.tgz",
- "integrity": "sha512-1ZmOXwJva14jCcTHM8jmsEBp33CCLng/tXK8/554ACwL3Kk4kbtdLfUjM/VEMZ3v3c1D7cJWxyYfTav5meumxg==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.4.2.tgz",
+ "integrity": "sha512-3PsiO4zEGgco/pSkYnHIB2j/IEnxsaoME+WdRYa8nRfewASAqCqf7e8DyOCftR7CBsXRosiUQWDcICu3cIfBgw==",
"cpu": [
"x64"
],
@@ -4404,9 +4355,9 @@
}
},
"node_modules/@nx/nx-freebsd-x64": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.8.6.tgz",
- "integrity": "sha512-1a681ZqSS05H1pC6JG3ae0BLhnxGtISkCigl9R6W5NeyFLBgP+Y4BLh+H9cCAlKzzLwiKWWRmhbxvjpnlhzB+w==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.4.2.tgz",
+ "integrity": "sha512-FXaQqn67KDGF6b735GCjFVyWVFWYrVxftvmaM/V4pCmJXjhO3K9NV3jhPVj2MNmrpdYwUtfTP1JMpr/iUBYCQA==",
"cpu": [
"x64"
],
@@ -4421,9 +4372,9 @@
}
},
"node_modules/@nx/nx-linux-arm-gnueabihf": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.8.6.tgz",
- "integrity": "sha512-qGztEgbEjMsFr9IjedQXJNmXLHCpSldW/sEtXoVZ8tXIzGr86GXbv+mLdZSZHrlJaNOq0y2K6XpVd2UH4ndwnQ==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.4.2.tgz",
+ "integrity": "sha512-RcVr6VN7lWJybr0bjs2zaK9mQ0OMFmuILx/8IDniLjAQK8JB+1qQhHLgunAAUJtWv+o0sVb6WXlN/F7PTegmEA==",
"cpu": [
"arm"
],
@@ -4438,9 +4389,9 @@
}
},
"node_modules/@nx/nx-linux-arm64-gnu": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.8.6.tgz",
- "integrity": "sha512-rSwsEISx5odXkg1kjXBZ6kjXCnM3fnAA+8YU1muRr7PmhUfM/zuCnNYcwmjtCRc7rRYBKzxmyE3T95fGK/NOIg==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.4.2.tgz",
+ "integrity": "sha512-Gt38hdU615g+pUAUHe5Z9ingLgpDKNumbJfqe6Y65N9XDHMGvi3YpUwFio2t/8DNZDYY7FH46CBYydDCJjDNyw==",
"cpu": [
"arm64"
],
@@ -4455,9 +4406,9 @@
}
},
"node_modules/@nx/nx-linux-arm64-musl": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.8.6.tgz",
- "integrity": "sha512-7rW21+uFj5KJx3z/HXhl6PUcp8+mQ8r/nUGbS59HjmMdVMZDd7PZKUVJF9Tu1ESproOCYSeJbOVk4WGiHtbF9Q==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.4.2.tgz",
+ "integrity": "sha512-Kp658KNoRfhi4a/1eoXrxxBiw2kkXqR745iuytVn1f/BL3L2tUHCp6+OyFF7sLx8TnlU9yZAxO62k4DPqS+Ffw==",
"cpu": [
"arm64"
],
@@ -4472,9 +4423,9 @@
}
},
"node_modules/@nx/nx-linux-x64-gnu": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.8.6.tgz",
- "integrity": "sha512-2/5WDr2wwWyvbqlB//ICWS5q3rRF4GyNX2NOp/tVkmh1RfDhH0ZAVZ/oJ7QvE1mKLQh0AM7bQBHsF5ikmMhUXw==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.4.2.tgz",
+ "integrity": "sha512-v+qOF2tmFFPX3fYYCqcdLIgATqlaQcBSHDs8EbwZjdncWk6RQAI/hq6+06+oZQc71RnyhBq5zBE12P0Bj1qTbw==",
"cpu": [
"x64"
],
@@ -4489,9 +4440,9 @@
}
},
"node_modules/@nx/nx-linux-x64-musl": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.8.6.tgz",
- "integrity": "sha512-G3UIMk+C090WR/btOaJCrBgRa7gjTj6ZBHinFceO7rii8r3D1SiN5cW1Njd1pV2K7IjJaSTuRtd9c1eLcIj9rQ==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.4.2.tgz",
+ "integrity": "sha512-MxlAqNItkSyiVcB91pOpYWX2Mj6PL9+GzPa63TA0v4PcpZTsFmToYlbKno/1e2T6AKI/0R1ZkAo1XxurUc++nw==",
"cpu": [
"x64"
],
@@ -4506,9 +4457,9 @@
}
},
"node_modules/@nx/nx-win32-arm64-msvc": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.8.6.tgz",
- "integrity": "sha512-8dfUstJkN2ChbIcj3TfcHgWyJy0b9za+3gU9IvZm82P9EeDCjEGoE/ld9VALGa+2UnX2Ve5BqlWGTD8BqYTeCA==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.4.2.tgz",
+ "integrity": "sha512-0FkvctI4lXFK0BEhQjM5If9RC0ja16oVjSacyLY893gBhbSI56Ud/XSA75uF6aplA4AvBe97NPQg5l5btJSxYw==",
"cpu": [
"arm64"
],
@@ -4523,9 +4474,9 @@
}
},
"node_modules/@nx/nx-win32-x64-msvc": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.8.6.tgz",
- "integrity": "sha512-kbWDZGD9kwP60UykTnfMR1hOUMDK0evXb5EnF4MAf4o18+b5KSzHyaL2TyNl+3s6lYdtZ2kYC679R+eJErKG8w==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.4.2.tgz",
+ "integrity": "sha512-J7Nh/3hfdlbEXvvIYJI+tAnvupYaeDwSU8ZRlDV7VU5Ee9VLT3hDLhmtXcDjEZnFHNPyaIYgFZXXDppU3a04Xg==",
"cpu": [
"x64"
],
@@ -5009,26 +4960,10 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@scure/bip39": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
- "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "@noble/hashes": "~1.1.1",
- "@scure/base": "~1.1.0"
- }
- },
- "node_modules/@scure/bip39/node_modules/@scure/base": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
- "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+ "node_modules/@scure/base": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
+ "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
@@ -5114,13 +5049,13 @@
}
},
"node_modules/@sigstore/protobuf-specs": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz",
- "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==",
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz",
+ "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^16.14.0 || >=18.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/@sigstore/sign": {
@@ -5177,21 +5112,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@stacks/auth": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/@stacks/auth/-/auth-7.0.4.tgz",
- "integrity": "sha512-e875v255+9mKv3v5tQIRBcyvnFXvdYcAX4y/7HcVjPAVKe8IPR938zURTmMlY3yehtXMyG1G5LFizi7qERsP7A==",
- "license": "MIT",
- "dependencies": {
- "@noble/secp256k1": "1.7.1",
- "@stacks/common": "^7.0.2",
- "@stacks/encryption": "^7.0.4",
- "@stacks/network": "^7.0.2",
- "@stacks/profile": "^7.0.4",
- "cross-fetch": "^3.1.5",
- "jsontokens": "^4.0.1"
- }
- },
"node_modules/@stacks/common": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@stacks/common/-/common-7.0.2.tgz",
@@ -5210,22 +5130,6 @@
"resolved": "packages/connect-ui",
"link": true
},
- "node_modules/@stacks/encryption": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-7.0.4.tgz",
- "integrity": "sha512-MIoRURjfIWN/1WpUGthY1jz7gCN/A76VV6G04MGw7Haiy1jW3Vhprf0kkoYKcQSu+NPyE9PWhaKjRXN4ZS/SwA==",
- "license": "MIT",
- "dependencies": {
- "@noble/hashes": "1.1.5",
- "@noble/secp256k1": "1.7.1",
- "@scure/bip39": "1.1.0",
- "@stacks/common": "^7.0.2",
- "base64-js": "^1.5.1",
- "bs58": "^5.0.0",
- "ripemd160-min": "^0.0.6",
- "varuint-bitcoin": "^1.1.2"
- }
- },
"node_modules/@stacks/eslint-config": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@stacks/eslint-config/-/eslint-config-2.0.0.tgz",
@@ -7934,9 +7838,9 @@
"license": "BSD-2-Clause"
},
"node_modules/@yarnpkg/parsers": {
- "version": "3.0.0-rc.46",
- "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz",
- "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz",
+ "integrity": "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -7944,7 +7848,7 @@
"tslib": "^2.4.0"
},
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.12.0"
}
},
"node_modules/@zkochan/js-yaml": {
@@ -8814,15 +8718,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/bs58": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
- "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
- "license": "MIT",
- "dependencies": {
- "base-x": "^4.0.0"
- }
- },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -11790,13 +11685,13 @@
}
},
"node_modules/dotenv-expand": {
- "version": "11.0.6",
- "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz",
- "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==",
+ "version": "11.0.7",
+ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
+ "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "dotenv": "^16.4.4"
+ "dotenv": "^16.4.5"
},
"engines": {
"node": ">=12"
@@ -11806,9 +11701,9 @@
}
},
"node_modules/dotenv-expand/node_modules/dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -13122,9 +13017,9 @@
}
},
"node_modules/exponential-backoff": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
- "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
+ "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
"dev": true,
"license": "Apache-2.0"
},
@@ -14598,6 +14493,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globals/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
@@ -16620,17 +16528,17 @@
}
},
"node_modules/lerna": {
- "version": "8.1.8",
- "resolved": "https://registry.npmjs.org/lerna/-/lerna-8.1.8.tgz",
- "integrity": "sha512-Rmo5ShMx73xM2CUcRixjmpZIXB7ZFlWEul1YvJyx/rH4onAwDHtUGD7Rx4NZYL8QSRiQHroglM2Oyq+WqA4BYg==",
+ "version": "8.1.9",
+ "resolved": "https://registry.npmjs.org/lerna/-/lerna-8.1.9.tgz",
+ "integrity": "sha512-ZRFlRUBB2obm+GkbTR7EbgTMuAdni6iwtTQTMy7LIrQ4UInG44LyfRepljtgUxh4HA0ltzsvWfPkd5J1DKGCeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@lerna/create": "8.1.8",
+ "@lerna/create": "8.1.9",
"@npmcli/arborist": "7.5.4",
"@npmcli/package-json": "5.2.0",
"@npmcli/run-script": "8.1.0",
- "@nx/devkit": ">=17.1.2 < 20",
+ "@nx/devkit": ">=17.1.2 < 21",
"@octokit/plugin-enterprise-rest": "6.0.1",
"@octokit/rest": "19.0.11",
"aproba": "2.0.0",
@@ -16644,7 +16552,7 @@
"conventional-changelog-angular": "7.0.0",
"conventional-changelog-core": "5.0.1",
"conventional-recommended-bump": "7.0.1",
- "cosmiconfig": "^8.2.0",
+ "cosmiconfig": "9.0.0",
"dedent": "1.5.3",
"envinfo": "7.13.0",
"execa": "5.0.0",
@@ -16675,7 +16583,7 @@
"npm-package-arg": "11.0.2",
"npm-packlist": "8.0.2",
"npm-registry-fetch": "^17.1.0",
- "nx": ">=17.1.2 < 20",
+ "nx": ">=17.1.2 < 21",
"p-map": "4.0.0",
"p-map-series": "2.1.0",
"p-pipe": "3.1.0",
@@ -16786,33 +16694,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lerna/node_modules/cosmiconfig": {
- "version": "8.3.6",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
- "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "import-fresh": "^3.3.0",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.2.0",
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- },
- "peerDependencies": {
- "typescript": ">=4.9.5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/lerna/node_modules/fs-extra": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
@@ -17066,9 +16947,9 @@
}
},
"node_modules/libnpmpublish/node_modules/ci-info": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz",
- "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
+ "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
"dev": true,
"funding": [
{
@@ -18257,9 +18138,9 @@
}
},
"node_modules/node-gyp": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz",
- "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==",
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz",
+ "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -18544,17 +18425,16 @@
}
},
"node_modules/nx": {
- "version": "19.8.6",
- "resolved": "https://registry.npmjs.org/nx/-/nx-19.8.6.tgz",
- "integrity": "sha512-VkEbXoCil4UnSDOJP5OcIKZgI13hKsFlQNf6oKhUHCYWoEHvVqpvabMv/ZY9mGG78skvqAorzn85BS3evlt0Cw==",
+ "version": "20.4.2",
+ "resolved": "https://registry.npmjs.org/nx/-/nx-20.4.2.tgz",
+ "integrity": "sha512-WXbKqk8looDo9zAISfmWtGyGm5RlOvr0G/THAa1WGSU4qHAZDsUtMAtwnxXje9s+R5rrwMmhbXCVvZELyeJP9Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@napi-rs/wasm-runtime": "0.2.4",
- "@nrwl/tao": "19.8.6",
"@yarnpkg/lockfile": "^1.1.0",
- "@yarnpkg/parsers": "3.0.0-rc.46",
+ "@yarnpkg/parsers": "3.0.2",
"@zkochan/js-yaml": "0.0.7",
"axios": "^1.7.4",
"chalk": "^4.1.0",
@@ -18576,13 +18456,14 @@
"npm-run-path": "^4.0.1",
"open": "^8.4.0",
"ora": "5.3.0",
+ "resolve.exports": "2.0.3",
"semver": "^7.5.3",
"string-width": "^4.2.3",
- "strong-log-transformer": "^2.1.0",
"tar-stream": "~2.2.0",
"tmp": "~0.2.1",
"tsconfig-paths": "^4.1.2",
"tslib": "^2.3.0",
+ "yaml": "^2.6.0",
"yargs": "^17.6.2",
"yargs-parser": "21.1.1"
},
@@ -18591,16 +18472,16 @@
"nx-cloud": "bin/nx-cloud.js"
},
"optionalDependencies": {
- "@nx/nx-darwin-arm64": "19.8.6",
- "@nx/nx-darwin-x64": "19.8.6",
- "@nx/nx-freebsd-x64": "19.8.6",
- "@nx/nx-linux-arm-gnueabihf": "19.8.6",
- "@nx/nx-linux-arm64-gnu": "19.8.6",
- "@nx/nx-linux-arm64-musl": "19.8.6",
- "@nx/nx-linux-x64-gnu": "19.8.6",
- "@nx/nx-linux-x64-musl": "19.8.6",
- "@nx/nx-win32-arm64-msvc": "19.8.6",
- "@nx/nx-win32-x64-msvc": "19.8.6"
+ "@nx/nx-darwin-arm64": "20.4.2",
+ "@nx/nx-darwin-x64": "20.4.2",
+ "@nx/nx-freebsd-x64": "20.4.2",
+ "@nx/nx-linux-arm-gnueabihf": "20.4.2",
+ "@nx/nx-linux-arm64-gnu": "20.4.2",
+ "@nx/nx-linux-arm64-musl": "20.4.2",
+ "@nx/nx-linux-x64-gnu": "20.4.2",
+ "@nx/nx-linux-x64-musl": "20.4.2",
+ "@nx/nx-win32-arm64-msvc": "20.4.2",
+ "@nx/nx-win32-x64-msvc": "20.4.2"
},
"peerDependencies": {
"@swc-node/register": "^1.8.0",
@@ -18669,9 +18550,9 @@
"license": "MIT"
},
"node_modules/nx/node_modules/dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -21719,6 +21600,16 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
+ "node_modules/resolve.exports": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -21817,14 +21708,6 @@
"node": "*"
}
},
- "node_modules/ripemd160-min": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
- "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/rollup": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
@@ -21935,6 +21818,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -22488,9 +22372,9 @@
}
},
"node_modules/spdx-license-ids": {
- "version": "3.0.20",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz",
- "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==",
+ "version": "3.0.21",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
+ "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
"dev": true,
"license": "CC0-1.0"
},
@@ -24011,13 +23895,12 @@
}
},
"node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
+ "version": "4.34.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.34.1.tgz",
+ "integrity": "sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==",
"license": "(MIT OR CC0-1.0)",
"engines": {
- "node": ">=10"
+ "node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -24517,15 +24400,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/varuint-bitcoin": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
- "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.1"
- }
- },
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
@@ -26526,18 +26400,17 @@
},
"packages/connect": {
"name": "@stacks/connect",
- "version": "7.10.0",
+ "version": "7.10.1",
"license": "MIT",
"dependencies": {
- "@stacks/auth": "^7.0.0",
+ "@scure/base": "^1.2.4",
"@stacks/common": "^7.0.0",
"@stacks/connect-ui": "6.6.0",
"@stacks/network": "^7.0.0",
"@stacks/network-v6": "npm:@stacks/network@^6.16.0",
"@stacks/profile": "^7.0.0",
"@stacks/transactions": "^7.0.0",
- "@stacks/transactions-v6": "npm:@stacks/transactions@^6.16.0",
- "jsontokens": "^4.0.1"
+ "@stacks/transactions-v6": "npm:@stacks/transactions@^6.16.0"
},
"devDependencies": {
"@babel/preset-env": "^7.23.3",
@@ -26565,11 +26438,10 @@
},
"packages/connect-react": {
"name": "@stacks/connect-react",
- "version": "22.6.1",
+ "version": "22.6.2",
"license": "MIT",
"dependencies": {
- "@stacks/connect": "7.10.0",
- "jsontokens": "^4.0.1"
+ "@stacks/connect": "7.10.1"
},
"devDependencies": {
"@types/react-dom": "^18.3.1",
diff --git a/package.json b/package.json
index b8a52a2a..6876ea3d 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-unused-imports": "^3.2.0",
"husky": "^9.1.6",
- "lerna": "8.1.8",
+ "lerna": "8.1.9",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"react": "^18.3.1",
@@ -48,5 +48,8 @@
"typedoc": "^0.26.10",
"typescript": "^5.3.2",
"vitest": "^2.1.8"
+ },
+ "dependencies": {
+ "type-fest": "^4.34.1"
}
}
diff --git a/packages/connect-react/package.json b/packages/connect-react/package.json
index b302cb7a..71d7149e 100644
--- a/packages/connect-react/package.json
+++ b/packages/connect-react/package.json
@@ -9,8 +9,7 @@
"types": "tsc --project tsconfig.json --emitDeclarationOnly"
},
"dependencies": {
- "@stacks/connect": "7.10.1",
- "jsontokens": "^4.0.1"
+ "@stacks/connect": "7.10.1"
},
"devDependencies": {
"@types/react-dom": "^18.3.1",
diff --git a/packages/connect-react/src/react/hooks/use-connect.ts b/packages/connect-react/src/react/hooks/use-connect.ts
index 5affc415..76c9d471 100644
--- a/packages/connect-react/src/react/hooks/use-connect.ts
+++ b/packages/connect-react/src/react/hooks/use-connect.ts
@@ -10,23 +10,23 @@ import {
FinishedAuthData,
openContractCall,
openContractDeploy,
- openSignTransaction,
openProfileUpdateRequestPopup,
openPsbtRequestPopup,
openSignatureRequestPopup,
+ openSignTransaction,
openStructuredDataSignatureRequestPopup,
openSTXTransfer,
- PsbtRequestOptions,
ProfileUpdateRequestOptions,
+ PsbtRequestOptions,
showBlockstackConnect,
SignatureRequestOptions,
+ SignTransactionOptions,
+ StacksProvider,
+ StructuredDataSignatureRequestOptions,
STXTransferOptions,
STXTransferRegularOptions,
STXTransferSponsoredOptions,
- StacksProvider,
- SignTransactionOptions,
} from '@stacks/connect';
-import { StructuredDataSignatureRequestOptions } from '@stacks/connect/src/types/structuredDataSignature';
import { useContext } from 'react';
import { ConnectContext, ConnectDispatchContext, States } from '../components/connect/context';
@@ -71,7 +71,7 @@ export const useConnect = () => {
void authenticate(_options, provider);
return;
} else {
- showBlockstackConnect({
+ void showBlockstackConnect({
...authOptions,
sendToSignIn: false,
});
diff --git a/packages/connect-ui/readme.md b/packages/connect-ui/README.md
similarity index 100%
rename from packages/connect-ui/readme.md
rename to packages/connect-ui/README.md
diff --git a/packages/connect-ui/src/components.d.ts b/packages/connect-ui/src/components.d.ts
index 7bedbaee..62c4057f 100644
--- a/packages/connect-ui/src/components.d.ts
+++ b/packages/connect-ui/src/components.d.ts
@@ -5,14 +5,14 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
-import { WebBTCProvider } from "./providers";
+import { WbipProvider } from "./providers";
export namespace Components {
interface ConnectModal {
"callback": Function;
"cancelCallback": Function;
- "defaultProviders": WebBTCProvider[];
- "installedProviders": WebBTCProvider[];
- "persistSelection": boolean;
+ "defaultProviders": WbipProvider[];
+ "installedProviders": WbipProvider[];
+ "persistWalletSelect": boolean;
}
}
declare global {
@@ -30,9 +30,9 @@ declare namespace LocalJSX {
interface ConnectModal {
"callback"?: Function;
"cancelCallback"?: Function;
- "defaultProviders"?: WebBTCProvider[];
- "installedProviders"?: WebBTCProvider[];
- "persistSelection"?: boolean;
+ "defaultProviders"?: WbipProvider[];
+ "installedProviders"?: WbipProvider[];
+ "persistWalletSelect"?: boolean;
}
interface IntrinsicElements {
"connect-modal": ConnectModal;
diff --git a/packages/connect-ui/src/components/modal/modal.tsx b/packages/connect-ui/src/components/modal/modal.tsx
index ff6ab0ab..39d01cf3 100644
--- a/packages/connect-ui/src/components/modal/modal.tsx
+++ b/packages/connect-ui/src/components/modal/modal.tsx
@@ -1,5 +1,5 @@
import { Component, Element, Prop, h } from '@stencil/core';
-import { WebBTCProvider, getProviderFromId } from '../../providers';
+import { WbipProvider, getProviderFromId } from '../../providers';
import { setSelectedProviderId } from '../../session';
import CloseIcon from './assets/close-icon.svg';
import { getBrowser, getPlatform } from './utils';
@@ -11,10 +11,10 @@ import { getBrowser, getPlatform } from './utils';
shadow: true,
})
export class Modal {
- @Prop() defaultProviders: WebBTCProvider[];
- @Prop() installedProviders: WebBTCProvider[];
+ @Prop() defaultProviders: WbipProvider[];
+ @Prop() installedProviders: WbipProvider[];
- @Prop() persistSelection: boolean;
+ @Prop() persistWalletSelect: boolean;
@Prop() callback: Function;
@Prop() cancelCallback: Function;
@@ -22,7 +22,7 @@ export class Modal {
@Element() modalEl: HTMLConnectModalElement;
handleSelectProvider(providerId: string) {
- if (this.persistSelection) setSelectedProviderId(providerId);
+ if (this.persistWalletSelect) setSelectedProviderId(providerId);
this.callback(getProviderFromId(providerId));
}
@@ -45,15 +45,15 @@ export class Modal {
// return null;
// }
- getBrowserUrl(provider: WebBTCProvider) {
+ getBrowserUrl(provider: WbipProvider) {
return provider.chromeWebStoreUrl ?? provider.mozillaAddOnsUrl;
}
- getMobileUrl(provider: WebBTCProvider) {
+ getMobileUrl(provider: WbipProvider) {
return provider.iOSAppStoreUrl ?? provider.googlePlayStoreUrl;
}
- getInstallUrl(provider: WebBTCProvider, browser: string, platform: string) {
+ getInstallUrl(provider: WbipProvider, browser: string, platform: string) {
if (platform === 'IOS') {
return provider.iOSAppStoreUrl ?? this.getBrowserUrl(provider) ?? provider.webUrl;
} else if (browser === 'Chrome') {
@@ -115,7 +115,7 @@ export class Modal {
Installed wallets
- {this.installedProviders.map((provider: WebBTCProvider) => (
+ {this.installedProviders.map((provider: WbipProvider) => (
-
![]({provider.icon})
@@ -184,7 +184,7 @@ export class Modal {
)}
- {notInstalledProviders.map((provider: WebBTCProvider) => (
+ {notInstalledProviders.map((provider: WbipProvider) => (
-
![]({provider.icon})
diff --git a/packages/connect-ui/src/components/modal/readme.md b/packages/connect-ui/src/components/modal/readme.md
index 0edab040..da29bec7 100644
--- a/packages/connect-ui/src/components/modal/readme.md
+++ b/packages/connect-ui/src/components/modal/readme.md
@@ -7,13 +7,13 @@
## Properties
-| Property | Attribute | Description | Type | Default |
-| -------------------- | ------------------- | ----------- | ------------------ | ----------- |
-| `callback` | -- | | `Function` | `undefined` |
-| `cancelCallback` | -- | | `Function` | `undefined` |
-| `defaultProviders` | -- | | `WebBTCProvider[]` | `undefined` |
-| `installedProviders` | -- | | `WebBTCProvider[]` | `undefined` |
-| `persistSelection` | `persist-selection` | | `boolean` | `undefined` |
+| Property | Attribute | Description | Type | Default |
+| --------------------- | ----------------------- | ----------- | ---------------- | ----------- |
+| `callback` | -- | | `Function` | `undefined` |
+| `cancelCallback` | -- | | `Function` | `undefined` |
+| `defaultProviders` | -- | | `WbipProvider[]` | `undefined` |
+| `installedProviders` | -- | | `WbipProvider[]` | `undefined` |
+| `persistWalletSelect` | `persist-wallet-select` | | `boolean` | `undefined` |
----------------------------------------------
diff --git a/packages/connect-ui/src/providers.ts b/packages/connect-ui/src/providers.ts
index f2d9f27e..fc18d805 100644
--- a/packages/connect-ui/src/providers.ts
+++ b/packages/connect-ui/src/providers.ts
@@ -1,6 +1,11 @@
// AUTO REGISTERED PROVIDERS
-export interface WebBTCProvider {
+import { getSelectedProviderId } from './session';
+
+/** Backwards compatible alias for `WbipProvider` */
+export type WebBTCProvider = WbipProvider;
+
+export interface WbipProvider {
/** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */
id: string;
/** The name of the provider, as displayed to the user */
@@ -27,18 +32,23 @@ declare global {
* The provider objects are WBIP004 compliant.
* It may happen that no wallet implements this feature before `@stacks/connect` switches to `webbtc`.
*/
- webbtc_stx_providers?: WebBTCProvider[];
+ webbtc_stx_providers?: WbipProvider[];
+
+ /** @experimental @beta */
+ wbip_providers?: WbipProvider[];
}
}
export const getRegisteredProviders = () => {
if (typeof window === 'undefined') return [];
- if (!window.webbtc_stx_providers) return [];
- return window.webbtc_stx_providers;
+ const legacyProviders = window.webbtc_stx_providers || [];
+ const wbipProviders = window.wbip_providers || [];
+
+ return [...legacyProviders, ...wbipProviders];
};
-export const getInstalledProviders = (defaultProviders: WebBTCProvider[] = []) => {
+export const getInstalledProviders = (defaultProviders: WbipProvider[] = []) => {
if (typeof window === 'undefined') return [];
const registeredProviders = getRegisteredProviders();
@@ -55,6 +65,24 @@ export const getInstalledProviders = (defaultProviders: WebBTCProvider[] = []) =
return registeredProviders.concat(additionalInstalledProviders);
};
+/**
+ * Check if a wallet provider was previously selected via Connect.
+ * @returns `true` if a provider was selected, `false` otherwise.
+ */
+export const isProviderSelected = () => {
+ return !!getSelectedProviderId();
+};
+
+/**
+ * Get the currently selected wallet provider.
+ * Note that this will not return the default `window.StacksProvider` object.
+ * @returns The wallet provider object, or null if no provider is selected.
+ */
+export const getProvider = () => {
+ const providerId = getSelectedProviderId();
+ return getProviderFromId(providerId);
+};
+
export const getProviderFromId = (id: string | undefined) => {
return id?.split('.').reduce((acc, part) => acc?.[part], window);
};
diff --git a/packages/connect/README.md b/packages/connect/README.md
index 195100b7..cf78675c 100644
--- a/packages/connect/README.md
+++ b/packages/connect/README.md
@@ -1,11 +1,19 @@
# `@stacks/connect` [![npm](https://img.shields.io/npm/v/@stacks/connect)](https://www.npmjs.com/package/@stacks/connect)
-## 🐎 Getting Started
+> [!NOTE]
+> The `7.x.x` version may still be more well supported by some wallets.
-### 1. Add the dependency
+For the legacy version of `@stacks/connect` using JWT tokens, please use the following command:
-Add the `@stacks/connect` dependency to your project using your favorite package manager.
-_Some options below_
+```sh
+npm install @stacks/connect@7.10.1
+```
+
+---
+
+## Getting Started
+
+### Install `@stacks/connect`
```shell
npm install @stacks/connect
@@ -13,213 +21,164 @@ pnpm install @stacks/connect
yarn add @stacks/connect
```
-### 2. Creating `AppConfig` and `UserSession`
-
-Add a reusable `UserSession` instance to your project.
-This will allow your website to store authentication state in localStorage.
+### Use `request` to trigger wallet interactions
```js
-/* ./userSession.js */
-import { AppConfig, UserSession } from '@stacks/connect';
+import { request } from '@stacks/connect';
-const appConfig = new AppConfig(['store_write', 'publish_data']);
-export const userSession = new UserSession({ appConfig }); // we will use this export from other files
+// CONNECT
+const response = await request('getAddresses');
```
-### 3. Interacting with the wallet
+### Available methods
-- ["Connect" aka authentication (`showConnect`)](#connect-aka-authentication-showconnect)
-- [Sending STX (`openSTXTransfer`)](#sending-stx-openstxtransfer)
-- [Calling Smart-Contracts (`openContractCall`)](#calling-smart-contracts-opencontractcall)
-- [Sending transactions with post-conditions (`openContractCall`)](#sending-transactions-with-post-conditions-opencontractcall)
- - [Post-Condition Modes](#post-condition-modes)
+- [`getAddresses`](#getaddresses)
+- [`stx_getAddresses`](#stx_getaddresses)
+- [`stx_getAccounts`](#stx_getaccounts)
+- [`stx_transferStx`](#stx_transferstx)
+- [`stx_callContract`](#stx_callcontract)
+- [`stx_deployContract`](#stx_deploycontract)
+- [`stx_signMessage`](#stx_signmessage)
+- [`stx_signStructuredMessage`](#stx_signstructuredmessage)
-#### "Connect" aka authentication (`showConnect`)
+#### `getAddresses`
-Connecting the wallet is a very simple form of authentication.
-This process gives the web-app information about a wallet account (selected by the user).
+```js
+const response = await request('getAddresses');
+// {
+// "addresses": [
+// {
+// "address": "bc1pp3ha248m0mnaevhp0txfxj5xaxmy03h0j7zuj2upg34mt7s7e32q7mdfae",
+// "publicKey": "062bd2c825300d74f4f9feb6b2fec2590beac02b8938f0fc042a34254581ee69",
+// },
+// {
+// "address": "bc1qtmqe7hg4etkq4t384nzg0mrmwf2sam9fjsz0mr",
+// "publicKey": "025b65a0ec0e00699794847f2af1b5d8a53db02a2f48e09417598bef09cfea1114",
+// },
+// {
+// "address": "SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN",
+// "publicKey": "02d3331cbb9f72fe635e6f87c2cf1a13cdea520f08c0cc68584a96e8ac19d8d304",
+// }
+// ]
+// }
+```
-The snippet below lets your web-app trigger the wallet to open and _authenticate_ an account.
-If no wallet is installed, an informational modal will be displayed in the web-app.
+#### `stx_getAddresses`
```js
-import { showConnect } from '@stacks/connect';
-import { userSession } from './userSession';
-
-const myAppName = 'My Stacks Web-App'; // shown in wallet pop-up
-const myAppIcon = window.location.origin + '/my_logo.png'; // shown in wallet pop-up
-
-showConnect({
- userSession, // `userSession` from previous step, to access storage
- appDetails: {
- name: myAppName,
- icon: myAppIcon,
- },
- onFinish: () => {
- window.location.reload(); // WHEN user confirms pop-up
- },
- onCancel: () => {
- console.log('oops'); // WHEN user cancels/closes pop-up
- },
-});
+const response = await request('stx_getAddresses');
+// {
+// "addresses": [
+// {
+// "address": "bc1pp3ha248m0mnaevhp0txfxj5xaxmy03h0j7zuj2upg34mt7s7e32q7mdfae",
+// "publicKey": "062bd2c825300d74f4f9feb6b2fec2590beac02b8938f0fc042a34254581ee69",
+// },
+// {
+// "address": "bc1qtmqe7hg4etkq4t384nzg0mrmwf2sam9fjsz0mr",
+// "publicKey": "025b65a0ec0e00699794847f2af1b5d8a53db02a2f48e09417598bef09cfea1114",
+// },
+// {
+// "address": "SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN",
+// "publicKey": "02d3331cbb9f72fe635e6f87c2cf1a13cdea520f08c0cc68584a96e8ac19d8d304",
+// }
+// ]
+// }
```
-#### Sending STX (`openSTXTransfer`)
+#### `stx_getAccounts`
-Sending STX tokens is also possible through web-apps interacting with a user's wallet.
+```js
+const response = await request('stx_getAccounts');
+// {
+// "addresses": [
+// {
+// "address": "SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN",
+// "publicKey": "02d3331cbb9f72fe635e6f87c2cf1a13cd...",
+// "gaiaHubUrl": "https://hub.hiro.so",
+// "gaiaAppKey": "0488ade4040658015580000000dc81e3a5..."
+// }
+// ]
+// }
+```
-The snippet below will open the wallet to _confirm and broadcast_ a smart-contract transaction.
-Here, we are sending `10000` micro-STX tokens to a recipient address.
+#### `stx_transferStx`
```js
-import { openSTXTransfer } from '@stacks/connect';
-import { AnchorMode, PostConditionMode } from '@stacks/transactions';
-import { userSession } from './userSession';
-
-openSTXTransfer({
- network: 'testnet', // which network to use; ('mainnet' or 'testnet')
- anchorMode: AnchorMode.Any, // which type of block the tx should be mined in
-
- recipient: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // which address we are sending to
- amount: 10000, // tokens, denominated in micro-STX
- memo: 'Nr. 1337', // optional; a memo to help identify the tx
-
- onFinish: response => {
- // WHEN user confirms pop-up
- console.log(response.txid); // the response includes the txid of the transaction
- },
- onCancel: () => {
- // WHEN user cancels/closes pop-up
- console.log('User canceled');
- },
+const response = await request('stx_transferStx', {
+ amount: '1000', // amount in micro-STX (1 STX = 1,000,000 micro-STX)
+ recipient: 'SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN', // recipient address
+ network: 'mainnet', // optional, defaults to mainnet
+ memo: 'Optional memo', // optional memo field
});
+// {
+// "txid": "0x1234...", // The transaction ID
+// }
```
-#### Calling Smart-Contracts (`openContractCall`)
-
-Calling smart-contracts lets users interact with the blockchain through transactions.
-
-The snippet below will open the wallet to _confirm and broadcast_ a smart-contract transaction.
-Here, we are passing our pick `Alice` to an imaginary deployed voting smart-contract.
+#### `stx_callContract`
```js
-import { openContractCall } from '@stacks/connect';
-import { AnchorMode, PostConditionMode, stringUtf8CV } from '@stacks/transactions';
-import { userSession } from './userSession';
-
-const pick = stringUtf8CV('Alice');
-
-openContractCall({
- network: 'testnet', // which network to use; ('mainnet' or 'testnet')
- anchorMode: AnchorMode.Any, // which type of block the tx should be mined in
-
- contractAddress: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK',
- contractName: 'example-contract',
- functionName: 'vote',
- functionArgs: [pick],
-
- postConditionMode: PostConditionMode.Deny, // whether the tx should fail when unexpected assets are transferred
- postConditions: [], // for an example using post-conditions, see next example
-
- onFinish: response => {
- // WHEN user confirms pop-up
- },
- onCancel: () => {
- // WHEN user cancels/closes pop-up
- },
+const response = await request('stx_callContract', {
+ contract: 'SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN.counters', // contract in format: address.contract-name
+ functionName: 'count', // name of the function to call
+ functionArgs: [Cl.int(3)], // array of Clarity values as arguments
+ network: 'mainnet', // optional, defaults to mainnet
});
+// {
+// "txid": "0x1234...", // The transaction ID
+// }
```
-#### Sending transactions with post-conditions (`openContractCall`)
-
-Consider the example above.
-Using [post-conditions](https://docs.hiro.so/get-started/transactions#post-conditions), a feature of the Stacks blockchain, we can ensure something happened after a transaction.
-Here, we could ensure that the recipient indeed receives a certain amount of STX.
+#### `stx_deployContract`
```js
-import {
- PostConditionMode,
- FungibleConditionCode,
- makeStandardSTXPostCondition,
-} from '@stacks/transactions';
-
-// this post-condition ensures that our recipient receives at least 5000 STX tokens
-const myPostCondition = makeStandardSTXPostCondition(
- 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // address of recipient
- FungibleConditionCode.GreaterEqual, // comparator
- 5000000000 // relative amount to previous balance (denoted in micro-STX)
-);
-
-// passing to `openContractCall` options, e.g. modifying our previous example ...
- postConditionMode: PostConditionMode.Deny, // whether the tx should fail when unexpected assets are transferred
- postConditions: [ myPostCondition ],
-// ...
+const response = await request('stx_deployContract', {
+ name: 'counters', // name of the contract
+ clarityCode: `(define-map counters principal int)
+
+(define-public (count (change int))
+ (ok (map-set counters tx-sender (+ (get-count tx-sender) change)))
+)
+
+(define-read-only (get-count (who principal))
+ (default-to 0 (map-get? counters who))
+)`, // Clarity code as string
+ clarityVersion: '2', // optional, defaults to latest version
+ network: 'mainnet', // optional, defaults to mainnet
+});
+// {
+// "txid": "0x1234...", // The transaction ID
+// }
```
-> For more examples on constructing different kinds of post-conditions read the [Post-Conditions Guide of Stacks.js](https://github.com/hirosystems/stacks.js/tree/main/packages/transactions#post-conditions).
-
-##### Post-Condition Modes
-
-If post-conditions `postConditions: [ ... ]` are specified, they will ALWAYS be checked by blockchain nodes.
-If ANY conditions fails, the transaction will fail.
-
-The _Post-Condition Mode_ only relates to transfers of assets, which were not specified in the `postConditions`.
-
-- `PostConditionMode.Deny` fails the transaction if any unspecified assets are transferred
-- `PostConditionMode.Allow` allows unspecified assets to be transferred
-- In both cases, all `postConditions` are checked
-
-### 🛠 Advanced
-
-#### Opening a specific wallet
-
-By default, `@stacks/connect` defers to the `window.StacksProvider` object to interact with wallets.
-However, if multiple wallets are installed, they might interfere with each other.
-To avoid this, you can specify which wallet to use in the wallet interaction methods.
+#### `stx_signMessage`
```js
-// Only opens requests in Leather
-authenticate({ ...opts }, LeatherProvider);
-openPsbtRequestPopup({ ...opts }, LeatherProvider);
-openProfileUpdateRequestPopup({ ...opts }, LeatherProvider);
-openSignatureRequestPopup({ ...opts }, LeatherProvider);
-openStructuredDataSignatureRequestPopup({ ...opts }, LeatherProvider);
+const response = await request('stx_signMessage', {
+ message: 'Hello, World!', // message to sign
+});
+// {
+// "signature": "0x1234...", // The signature of the message
+// "publicKey": "02d3331cbb9f72fe635e6f87c2cf1a13cdea520f08c0cc68584a96e8ac19d8d304" // The public key that signed the message
+// }
```
-## 🤔 Pitfalls
-
-- Connect can currently not set manual nonces, since this is not supported by wallets.
-- For some projects it might be necessary to also install the `regenerator-runtime` package. `npm install --save-dev regenerator-runtime`. This is a build issue of older versions of `@stacks/connect`.
-
-## 📚 Method Parameters
-
-A glossary of the most common options of `openSTXTransfer` and `openContractCall`
+#### `stx_signStructuredMessage`
-### `openSTXTransfer` _Required_
-
-| | Description | Type | Example |
-| :---------- | :------------------------------------ | :-------------------------------- | :-------------------------------------------- |
-| `recipient` | The recipient (STX principal) address | `string` | `'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK'` |
-| `amount` | The amount (in micro-STX) to transfer | Integer (e.g. `number`, `bigint`) | `10000` |
-
-### `openContractCall` _Required_
-
-| | Description | Type | Example |
-| :---------------- | :----------------------------------------------- | :---------------------- | :-------------------------------------------- |
-| `contractAddress` | The (STX contract) address of the smart contract | `string` | `'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK'` |
-| `contractName` | The contract name | `string` | `'example-contract'` |
-| `functionName` | The contract function name | `string` | `'vote'` |
-| `functionArgs` | The contract function arguments | Array of Clarity Values | `[]`, `[uintCV(100)]` |
-
-### _Optional_
+```js
+const clarityMessage = Cl.parse('{ structured: "message", num: u3 }');
+const clarityDomain = Cl.tuple({
+ domain: Cl.stringAscii('example.com'),
+ version: Cl.stringAscii('1.0.0'),
+ 'chain-id': Cl.uint(1),
+});
-| | Default | Description | Type | Example |
-| :------------------ | :------------------ | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------ | :------------------------ |
-| `network` | Mainnet | The network to broadcast the transaction to | string | 'mainnet' |
-| `anchorMode` | Any | The type of block the transaction should be mined in | [AnchorMode Enum](https://stacks.js.org/enums/transactions.AnchorMode.html) | `AnchorMode.OnChainOnly` |
-| `memo` | _Empty_ `''` | The memo field (used for additional data) | `string` | `'a memo'` |
-| `fee` | _Handled by Wallet_ | The transaction fee (the wallet will estimate fees as well) | Integer (e.g. `number`, `bigint`) | `1000` |
-| `postConditionMode` | Deny | The post condition mode, _i.e. whether to allow unspecified asset transfer_ | [PostConditionMode](https://stacks.js.org/enums/transactions.PostConditionMode) | `PostConditionMode.Allow` |
-| `postConditions` | _Empty_ `[]` | The list of post conditions to check, regardless of postConditionMode | [PostCondition](https://stacks.js.org/types/transactions.PostCondition)[] | |
-| `onFinish` | _No-op_ | The callback function to run after broadcasting the transaction | Function (receiving `response`) | |
-| `onCancel` | _No-op_ | The callback function to run after the user cancels/closes the wallet | Function | |
+const response = await request('stx_signStructuredMessage', {
+ message: clarityMessage, // Clarity value representing the structured message
+ domain: clarityDomain, // domain object for SIP-018 style signing
+});
+// {
+// "signature": "0x1234...", // The signature of the structured message
+// "publicKey": "02d3331cbb9f72fe635e6f87c2cf1a13cdea520f08c0cc68584a96e8ac19d8d304" // The public key that signed the message
+// }
+```
diff --git a/packages/connect/package.json b/packages/connect/package.json
index 587732fc..4097d9a2 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -4,6 +4,7 @@
"license": "MIT",
"scripts": {
"build": "concurrently 'tsup src/index.ts' 'npm run types'",
+ "dev": "tsc --project tsconfig.json --watch",
"prepublishOnly": "npm run build",
"typecheck": "tsc --project tsconfig.json --noEmit",
"types": "tsc --project tsconfig.json --emitDeclarationOnly",
@@ -14,15 +15,14 @@
"test:coverage": "vitest run --coverage"
},
"dependencies": {
- "@stacks/auth": "^7.0.0",
+ "@scure/base": "^1.2.4",
"@stacks/common": "^7.0.0",
"@stacks/connect-ui": "6.6.0",
"@stacks/network": "^7.0.0",
"@stacks/network-v6": "npm:@stacks/network@^6.16.0",
"@stacks/profile": "^7.0.0",
"@stacks/transactions": "^7.0.0",
- "@stacks/transactions-v6": "npm:@stacks/transactions@^6.16.0",
- "jsontokens": "^4.0.1"
+ "@stacks/transactions-v6": "npm:@stacks/transactions@^6.16.0"
},
"sideEffects": false,
"publishConfig": {
diff --git a/packages/connect/src/asigna.ts b/packages/connect/src/asigna.ts
index 1152ca11..80f0dae6 100644
--- a/packages/connect/src/asigna.ts
+++ b/packages/connect/src/asigna.ts
@@ -21,9 +21,7 @@ const AsignaIframeProvider = {
return generateCall(payload, 'transactionRequest');
},
};
-const generateAsignaMessage = (payload: string, key: string) => {
- return { source, [key]: payload };
-};
+const generateAsignaMessage = (payload: string, key: string) => ({ source, [key]: payload });
export const initializeAsignaProvider = () => {
if (typeof window === 'undefined') return;
diff --git a/packages/connect/src/auth.ts b/packages/connect/src/auth.ts
index a2037dc1..611ee311 100644
--- a/packages/connect/src/auth.ts
+++ b/packages/connect/src/auth.ts
@@ -1,15 +1,15 @@
-import { AppConfig, UserSession } from '@stacks/auth';
-import { decodeToken } from 'jsontokens';
-import type { AuthOptions, AuthResponsePayload, StacksProvider } from './types';
-
-import { getStacksProvider } from './utils';
+import { NoSessionDataError } from '@stacks/common';
+import { request } from './request';
+import { AuthOptions, StacksProvider } from './types';
+/** @deprecated Not used anymore. */
export const defaultAuthURL = 'https://app.blockstack.org';
if (typeof window !== 'undefined') {
window.__CONNECT_VERSION__ = '__VERSION__'; // replaced via tsup esbuildOptions
}
+/** @deprecated Will be marked as internal going forward. */
export const isMobile = () => {
const ua = navigator.userAgent;
if (/android/i.test(ua)) {
@@ -22,79 +22,337 @@ export const isMobile = () => {
};
/**
- * mobile should not use a 'popup' type of window.
+ * Special `authenticate` legacy request, to store addresses in userSession matching legacy behavior.
+ * @internal Legacy UI request.
*/
-export const shouldUsePopup = () => {
- return !isMobile();
-};
+export const authenticate = async (authOptions: AuthOptions, provider?: StacksProvider) => {
+ const { onFinish, onCancel, userSession: _userSession } = authOptions;
-export const getOrCreateUserSession = (userSession?: UserSession): UserSession => {
- if (!userSession) {
- const appConfig = new AppConfig(['store_write'], document.location.href);
- userSession = new UserSession({ appConfig });
- }
- return userSession;
-};
-
-export const authenticate = async (
- authOptions: AuthOptions,
- provider: StacksProvider = getStacksProvider()
-) => {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
-
- const {
- redirectTo = '/',
- manifestPath,
- onFinish,
- onCancel,
- sendToSignIn = false,
- userSession: _userSession,
- appDetails,
- } = authOptions;
const userSession = getOrCreateUserSession(_userSession);
- if (userSession.isUserSignedIn()) {
- userSession.signUserOut();
- }
- const transitKey = userSession.generateAndStoreTransitKey();
- const authRequest = userSession.makeAuthRequest(
- transitKey,
- `${document.location.origin}${redirectTo}`,
- `${document.location.origin}${manifestPath}`,
- userSession.appConfig.scopes,
- undefined,
- undefined,
- {
- sendToSignIn,
- appDetails,
- connectVersion: '__VERSION__', // replaced via tsup esbuildOptions,
- }
- );
+ if (userSession.isUserSignedIn()) userSession.signUserOut();
try {
- const authResponse = await provider.authenticationRequest(authRequest);
- await userSession.handlePendingSignIn(authResponse);
- const token = decodeToken(authResponse);
- const payload = token?.payload;
- const authResponsePayload = payload as unknown as AuthResponsePayload;
- onFinish?.({
- authResponse,
- authResponsePayload,
- userSession,
+ const method = 'getAddresses';
+ const response = await request({ provider, forceWalletSelect: true }, method);
+
+ const sessionData = userSession.store.getSessionData();
+
+ // Ensure user data structure exists
+ sessionData.userData ??= { profile: {} };
+ sessionData.userData.profile ??= {};
+ sessionData.userData.profile.stxAddress ??= {
+ mainnet: '',
+ testnet: '',
+ };
+
+ // Take first STX address and use it for legacy connect user session storing.
+ const address = response.addresses
+ .find(a => a?.symbol === 'STX' || a.address.startsWith('S'))
+ .address.toUpperCase();
+ const isMainnet = address[1] === 'P' || address[1] === 'M';
+
+ Object.assign(sessionData.userData.profile.stxAddress, {
+ [isMainnet ? 'mainnet' : 'testnet']: address,
});
+
+ // Take first BTC address and use it for legacy connect user session storing.
+ const btcAddress = response.addresses.find(a => {
+ if (a?.address?.startsWith('S')) return false;
+ if (a['purpose'] === 'payment') return true;
+
+ // If it's taproot, only select it if no non-taproot addresses exist
+ if (isAddressTaproot(a?.address)) {
+ const onlyTaprootAddressesExist = response.addresses.every(
+ addr => addr?.address?.startsWith('S') || isAddressTaproot(addr?.address)
+ );
+ return onlyTaprootAddressesExist;
+ }
+
+ return true;
+ })?.address;
+ if (btcAddress) sessionData.userData.profile.btcAddress = btcAddress;
+
+ userSession.store.setSessionData(sessionData);
+
+ onFinish?.({ userSession });
} catch (error) {
console.error('[Connect] Error during auth request', error);
- onCancel?.();
+ onCancel?.(error);
}
};
-// eslint-disable-next-line @typescript-eslint/require-await
-export const getUserData = async (userSession?: UserSession) => {
- userSession = getOrCreateUserSession(userSession);
- if (userSession.isUserSignedIn()) {
- return userSession.loadUserData();
+// Legacy User Session, User Data, etc.
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const LOCALSTORAGE_SESSION_KEY = 'blockstack-session';
+
+/**
+ * Semi-compatible `AppConfig` type for configuring `UserSession`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class AppConfig {
+ // Copied over from legacy @stacks/auth
+ constructor(
+ _scopes?: any,
+ _appDomain?: any,
+ _redirectPath?: string,
+ _manifestPath?: string,
+ _coreNode?: string,
+ _authenticatorURL?: string
+ ) {}
+}
+
+/**
+ * Semi-compatible `SessionOptions` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export interface SessionOptions {
+ userData?: UserData;
+ localStorageKey?: string;
+ storeOptions?: {
+ localStorageKey?: string;
+ };
+}
+
+/**
+ * Semi-compatible `SessionData` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export interface SessionData {
+ userData?: UserData;
+}
+
+/**
+ * Semi-compatible `UserSession` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class UserSession {
+ // Copied over from legacy @stacks/auth
+ appConfig: any;
+
+ store: SessionDataStore;
+
+ /**
+ * UserSession might still work for some use cases, but it's not recommended.
+ *
+ * @deprecated Update to the latest `request` RPC methods.
+ */
+ constructor(options?: { appConfig?: any; sessionStore?: any; sessionOptions?: SessionOptions }) {
+ if (options?.appConfig) this.appConfig = options.appConfig;
+
+ if (typeof window === 'undefined' && typeof self === 'undefined') {
+ // not running in browser
+ this.store = new InstanceDataStore();
+ } else {
+ // running in browser
+ this.store = new LocalStorageStore();
+ }
}
- if (userSession.isSignInPending()) {
- return userSession.handlePendingSignIn();
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ makeAuthRequestToken() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ generateAndStoreTransitKey() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ getAuthResponseToken() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ isSignInPending() {
+ return false;
+ }
+
+ /**
+ * Check if a user is currently signed in.
+ *
+ * @returns {Boolean} `true` if the user is signed in (aka connected), `false` if not.
+ */
+ isUserSignedIn() {
+ return !!this.store.getSessionData().userData;
+ }
+
+ /**
+ * Try to process any pending sign in request by returning a `Promise` that resolves
+ * to the user data object if the sign in succeeds.
+ *
+ * @returns {Promise} that resolves to the user data object if successful and rejects
+ * if handling the sign in request fails or there was no pending sign in request.
+ */
+ async handlePendingSignIn(): Promise
{
+ return Promise.resolve(this.loadUserData());
+ }
+
+ /**
+ * Retrieves the user data object. The user's profile is stored in the key [[Profile]].
+ *
+ * @returns {Object} User data object.
+ */
+ loadUserData() {
+ const userData = this.store.getSessionData().userData;
+ if (!userData) throw new NoSessionDataError('No user data found. Did the user sign in?');
+ return userData;
}
- return null;
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ encryptContent() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ decryptContent() {}
+
+ /**
+ * Sign the user out and optionally redirect to given location.
+ * @param redirectURL Location to redirect user to after sign out.
+ */
+ signUserOut(redirectURL?: string) {
+ this.store.deleteSessionData();
+ if (redirectURL && typeof location !== 'undefined' && location.href) {
+ location.href = redirectURL;
+ }
+ }
+}
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export interface UserData {
+ /** @deprecated */
+ email?: string;
+ /** @deprecated */
+ decentralizedID?: string;
+ /** @deprecated */
+ identityAddress?: string;
+ /** @deprecated */
+ appPrivateKey?: string;
+ /** @deprecated */
+ hubUrl?: string;
+ /** @deprecated */
+ coreNode?: string;
+ /** @deprecated */
+ authResponseToken?: string;
+ /** @deprecated */
+ coreSessionToken?: string;
+ /** @deprecated */
+ gaiaAssociationToken?: string;
+ /** @deprecated */
+ profile: any;
+ /** @deprecated */
+ gaiaHubConfig?: any;
+ /** @deprecated */
+ appPrivateKeyFromWalletSalt?: string;
+}
+
+/**
+ * @abstract An abstract class representing the SessionDataStore interface.
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class SessionDataStore {
+ constructor(sessionOptions?: SessionOptions) {
+ if (sessionOptions) this.setSessionData(sessionOptions);
+ }
+
+ getSessionData(): SessionData {
+ throw new Error('Abstract class');
+ }
+
+ setSessionData(_session: SessionData): boolean {
+ throw new Error('Abstract class');
+ }
+
+ deleteSessionData(): boolean {
+ throw new Error('Abstract class');
+ }
+}
+
+/**
+ * Stores session data in the instance of this class.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class InstanceDataStore extends SessionDataStore {
+ sessionData?: SessionData;
+
+ constructor(sessionOptions?: SessionOptions) {
+ super(sessionOptions);
+ if (!this.sessionData) this.setSessionData({});
+ }
+
+ getSessionData(): SessionData {
+ if (!this.sessionData) throw new NoSessionDataError('No session data was found.');
+ return this.sessionData;
+ }
+
+ setSessionData(session: SessionData): boolean {
+ this.sessionData = session;
+ return true;
+ }
+
+ deleteSessionData(): boolean {
+ this.setSessionData({});
+ return true;
+ }
+}
+
+/**
+ * Stores session data in browser a localStorage entry.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class LocalStorageStore extends SessionDataStore {
+ key: string;
+
+ constructor(sessionOptions?: SessionOptions) {
+ super(sessionOptions);
+ this.key =
+ typeof sessionOptions?.storeOptions?.localStorageKey === 'string'
+ ? sessionOptions.storeOptions.localStorageKey
+ : LOCALSTORAGE_SESSION_KEY;
+
+ const data = localStorage.getItem(this.key);
+ if (!data) this.setSessionData({});
+ }
+
+ getSessionData(): SessionData {
+ const data = localStorage.getItem(this.key);
+ if (!data) throw new NoSessionDataError('No session data was found in localStorage');
+
+ return JSON.parse(data);
+ }
+
+ setSessionData(session: SessionData): boolean {
+ localStorage.setItem(this.key, JSON.stringify(session));
+ return true;
+ }
+
+ deleteSessionData(): boolean {
+ localStorage.removeItem(this.key);
+ this.setSessionData({});
+ return true;
+ }
+}
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const getOrCreateUserSession = (userSession?: UserSession): UserSession => {
+ if (userSession) return userSession;
+ return new UserSession();
};
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const getUserData = async (userSession?: UserSession) => {
+ userSession = getOrCreateUserSession(userSession);
+ if (userSession.isUserSignedIn()) return Promise.resolve(userSession.loadUserData());
+ return Promise.resolve(null);
+};
+
+/** @internal */
+export function isAddressTaproot(address: string) {
+ const PREFIXES = ['bc1p', 'tb1p', 'bcrt1p'];
+ const LENGTHS = [62, 62, 64];
+
+ const index = PREFIXES.findIndex(p => address.startsWith(p));
+ if (index === -1) return false;
+
+ return address.length === LENGTHS[index];
+}
diff --git a/packages/connect/src/bitcoin/psbt.ts b/packages/connect/src/bitcoin/psbt.ts
index 51dee332..5bb4d593 100644
--- a/packages/connect/src/bitcoin/psbt.ts
+++ b/packages/connect/src/bitcoin/psbt.ts
@@ -1,79 +1,44 @@
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
+import { base64 } from '@scure/base';
+import { bytesToHex, hexToBytes } from '@stacks/common';
+import { MethodParams, MethodResult, Sighash } from '../methods';
+import { requestRawLegacy } from '../request';
import { StacksProvider } from '../types';
-import { PsbtPayload, PsbtPopup, PsbtRequestOptions } from '../types/bitcoin';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+import { PsbtData, PsbtRequestOptions, SignatureHash } from '../types/bitcoin';
+import { getStacksProvider } from '../utils';
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: PsbtPayload, 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 getDefaultPsbtRequestOptions(options: PsbtRequestOptions) {
- const network = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: PsbtRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- ...defaults,
- };
-}
-
-async function openPsbtPopup({ token, options }: PsbtPopup, provider: StacksProvider) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultPsbtRequestOptions(_options: PsbtRequestOptions) {}
- try {
- const psbtResponse = await provider.psbtRequest(token);
- options.onFinish?.(psbtResponse);
- } catch (error) {
- console.error('[Connect] Error during psbt request', error);
- options.onCancel?.();
- }
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-export const makePsbtToken = async (options: PsbtRequestOptions) => {
- const { allowedSighash, hex, signAtIndex, userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const makePsbtToken = async (_options: PsbtRequestOptions) => {};
- const payload: PsbtPayload = {
- ..._options,
- allowedSighash,
- hex,
- signAtIndex,
- publicKey,
- };
+const METHOD = 'signPsbt' as const;
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- return createUnsecuredToken(payload as Json);
-};
+/** @internal */
+export const LEGACY_SIGN_PSBT_OPTIONS_MAP = (
+ options: PsbtRequestOptions
+): MethodParams => ({
+ psbt: base64.encode(hexToBytes(options.hex)),
+ signInputs: typeof options.signAtIndex === 'number' ? [options.signAtIndex] : options.signAtIndex,
+ allowedSighash: options.allowedSighash?.map(hash => SignatureHash[hash] as Sighash),
+});
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultPsbtRequestOptions(options),
- ...options,
- } as T);
- return openPsbtPopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_SIGN_PSBT_RESPONSE_MAP = (response: MethodResult): PsbtData => ({
+ hex: bytesToHex(base64.decode(response.psbt)),
+});
/**
* @experimental
+ * Compatible interface with previous Connect `openPsbtRequestPopup` version, but using new SIP-030 RPC method.
*/
export function openPsbtRequestPopup(
options: PsbtRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- return generateTokenAndOpenPopup(options, makePsbtToken, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_PSBT_OPTIONS_MAP,
+ LEGACY_SIGN_PSBT_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/errors.ts b/packages/connect/src/errors.ts
new file mode 100644
index 00000000..ae97c1ed
--- /dev/null
+++ b/packages/connect/src/errors.ts
@@ -0,0 +1,74 @@
+import { JsonRpcResponseError } from './methods';
+
+export class JsonRpcError extends Error {
+ constructor(
+ public message: string,
+ public code: number,
+ public data?: string,
+
+ public cause?: Error
+ ) {
+ super(message);
+ this.name = 'JsonRpcError';
+ this.message = message;
+ this.code = code;
+ this.data = data;
+
+ this.cause = cause;
+ }
+
+ static fromResponse(error: JsonRpcResponseError) {
+ return new JsonRpcError(error.message, error.code, error.data);
+ }
+
+ toString() {
+ return `${this.name} (${this.code}): ${this.message}${this.data ? `: ${JSON.stringify(this.data)}` : ''}`;
+ }
+}
+
+/**
+ * Numeric error codes for JSON-RPC errors, used for `.code` in {@link JsonRpcError}.
+ * Implementation-defined wallet errors range from `-32099` to `-32000`.
+ */
+export enum JsonRpcErrorCode {
+ // https://www.jsonrpc.org/specification#error_object
+ // > The error codes from and including -32768 to -32000 are reserved for pre-defined errors.
+
+ /** Invalid JSON received by server while parsing */
+ ParseError = -32_700,
+
+ /** Invalid Request object */
+ InvalidRequest = -32_600,
+
+ /** Method not found/available */
+ MethodNotFound = -32_601,
+
+ /** Invalid method params */
+ InvalidParams = -32_602,
+
+ /** Internal JSON-RPC error */
+ InternalError = -32_603,
+
+ // IMPLEMENTATION-DEFINED WALLET ERRORS
+ /** User rejected the request (implementation-defined wallet error) */
+ UserRejection = -32_000,
+
+ /** Address mismatch for the requested method (implementation-defined wallet error) */
+ MethodAddressMismatch = -32_001,
+
+ /** Access denied for the requested method (implementation-defined wallet error) */
+ MethodAccessDenied = -32_002,
+
+ // CUSTOM ERRORS (Custom range, not inside the JSON-RPC error code range)
+ /**
+ * Unknown external error.
+ * Error does not originate from the wallet.
+ */
+ UnknownError = -31_000,
+
+ /**
+ * User canceled the request.
+ * Error may not originate from the wallet.
+ */
+ UserCanceled = -31_001,
+}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index 6edb16e1..dd5dba74 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -1,19 +1,53 @@
export * from './auth';
-export * from './bitcoin';
-export * from './transactions';
-export * from './signature';
-export * from './signature/structuredData';
-export * from './profile';
+export * from './providers';
export * from './types';
-export * from './utils';
export * from './ui';
-export * from './providers';
+// Manual exports to avoid exporting internals (e.g. `LEGACY_XYZ`)
+export { getDefaultPsbtRequestOptions, makePsbtToken, openPsbtRequestPopup } from './bitcoin';
+export {
+ getDefaultSignatureRequestOptions,
+ SignatureRequestPayload,
+ signMessage,
+ openSignatureRequestPopup,
+} from './signature';
+export {
+ signStructuredMessage,
+ openStructuredDataSignatureRequestPopup,
+} from './signature/structuredData';
+export {
+ getDefaultProfileUpdateRequestOptions,
+ makeProfileUpdateToken,
+ openProfileUpdateRequestPopup,
+} from './profile';
+export {
+ getUserSession,
+ hasAppPrivateKey,
+ getKeys,
+ getStxAddress,
+ makeContractCallToken,
+ makeContractDeployToken,
+ makeSTXTransferToken,
+ makeSignTransaction,
+ openContractCall,
+ openContractDeploy,
+ openSTXTransfer,
+ openSignTransaction,
+} from './transactions';
+export { request, requestRaw } from './request';
+export { getStacksProvider, isStacksWalletInstalled } from './utils';
+
+// TODO: (next)
+// We won't expose these types (TypeBox and Zod) until they are final and stable.
+// TypeBox
+// Only export the outermost typebox schemas
+// export { ClarityValueTypeBoxSchema, PostConditionTypeBoxSchema } from './types/typebox';
-// re-exports
-export * from '@stacks/auth';
+// Re-exports
export {
clearSelectedProviderId,
getSelectedProviderId,
setSelectedProviderId,
+ isProviderSelected,
} from '@stacks/connect-ui';
+export type { WebBTCProvider, WbipProvider } from '@stacks/connect-ui';
diff --git a/packages/connect/src/methods.ts b/packages/connect/src/methods.ts
new file mode 100644
index 00000000..79ce91bc
--- /dev/null
+++ b/packages/connect/src/methods.ts
@@ -0,0 +1,263 @@
+import type {
+ AddressString,
+ ClarityValue,
+ ContractIdString,
+ PostCondition,
+ PostConditionModeName,
+ TupleCV,
+} from '@stacks/transactions';
+import { LiteralUnion } from 'type-fest';
+
+// Re-export types from Stacks.js
+export type {
+ AddressString,
+ AssetString,
+ ClarityValue,
+ ContractIdString,
+ FungibleComparator,
+ FungiblePostCondition,
+ NonFungibleComparator,
+ NonFungiblePostCondition,
+ PostCondition,
+ PostConditionModeName,
+ StxPostCondition,
+} from '@stacks/transactions';
+
+// TYPES
+
+export type NetworkString = LiteralUnion<'mainnet' | 'testnet' | 'regtest' | 'devnet', string>;
+
+export type PrincipalString = AddressString | ContractIdString;
+
+export type Integer = number | bigint | string;
+
+export interface AddressEntry {
+ symbol?: string;
+ address: string;
+ publicKey: string;
+}
+
+export interface AccountEntry extends AddressEntry {
+ gaiaHubUrl: string;
+ gaiaAppKey: string;
+}
+
+// PARAMS
+
+interface CommonTxParams {
+ /**
+ * The recommended address to use for the method.
+ *
+ * ⚠︎ Warning: Wallets may not implement this for privacy reasons.
+ */
+ address?: AddressString;
+
+ network?: NetworkString;
+
+ fee?: Integer;
+ nonce?: Integer;
+
+ sponsored?: boolean;
+
+ postConditions?: (string | PostCondition)[]; // hex-encoded string or JSON PostCondition
+ postConditionMode?: PostConditionModeName;
+}
+
+export interface TransferStxParams
+ extends Omit {
+ recipient: string;
+ amount: Integer;
+ memo?: string;
+}
+
+export interface TransferFungibleParams extends CommonTxParams {
+ recipient: string;
+ asset: string;
+ amount: Integer;
+}
+
+export interface TransferNonFungibleParams extends CommonTxParams {
+ recipient: string;
+ asset: string;
+ assetId: string | ClarityValue; // todo: add string (hex-encoded), add string (clarity syntax)
+}
+
+export interface CallContractParams extends CommonTxParams {
+ contract: ContractIdString;
+ functionName: string;
+ functionArgs?: string[] | ClarityValue[]; // todo: add string (hex-encoded), add string (clarity syntax)
+}
+
+export interface DeployContractParams extends CommonTxParams {
+ name: string;
+ clarityCode: string;
+ clarityVersion?: number | string;
+}
+
+export interface SignTransactionParams {
+ transaction: string;
+ broadcast?: boolean; // todo: check before merging
+}
+
+export interface SignMessageParams {
+ message: string;
+}
+
+export interface SignStructuredMessageParams {
+ message: ClarityValue;
+ domain: TupleCV;
+}
+
+export interface GetAddressesParams {
+ network?: NetworkString;
+}
+
+export interface GetAccountsParams {
+ network?: NetworkString;
+}
+
+export interface UpdateProfileParams {
+ profile: Record;
+}
+
+// RESULTS
+
+export interface TransactionResult {
+ txid?: string;
+ transaction?: string; // todo: check before merging
+}
+
+export interface SignTransactionResult {
+ txid?: string;
+ transaction: string;
+}
+
+export interface SignMessageResult {
+ signature: string;
+ publicKey: string;
+}
+
+export interface GetAddressesResult {
+ addresses: AddressEntry[];
+}
+
+export interface GetAccountsResult {
+ accounts: AccountEntry[];
+}
+
+export interface UpdateProfileResult {
+ profile: Record;
+}
+
+// ERROR RESPONSES
+
+// todo: add error responses
+
+// JSON RPC METHODS
+
+/// BITCOIN METHODS
+
+export type Sighash = 'ALL' | 'NONE' | 'SINGLE' | 'ANYONECANPAY';
+
+export interface SignInputsByAddress {
+ index: number;
+ address?: string;
+ publicKey?: string;
+
+ /** @experimental Might need a rename, when wallets adopt SIPs/WBIPs. */
+ allowedSighash?: Sighash[];
+}
+
+export interface SignPsbtParams {
+ psbt: string;
+ signInputs?: number[] | SignInputsByAddress[];
+
+ /** @experimental Might need a rename, when wallets adopt SIPs/WBIPs. */
+ allowedSighash?: Sighash[];
+}
+
+export interface SignPsbtResult {
+ txid?: string;
+ psbt: string;
+}
+
+export type JsonRpcResponseError = {
+ code: number;
+ message: string;
+ data?: any;
+};
+
+// todo: double check spec
+export type JsonRpcResponse = {
+ jsonrpc: '2.0';
+ id: string | number | null;
+} & (
+ | {
+ result: Methods[M]['result'];
+ }
+ | {
+ error: JsonRpcResponseError;
+ }
+);
+
+export type Methods = {
+ // BTC
+ signPsbt: {
+ params: SignPsbtParams;
+ result: SignPsbtResult;
+ };
+ getAddresses: {
+ params: GetAddressesParams;
+ result: GetAddressesResult;
+ };
+
+ // STX
+ stx_transferStx: {
+ params: TransferStxParams;
+ result: TransactionResult;
+ };
+ stx_transferSip10Ft: {
+ params: TransferFungibleParams;
+ result: TransactionResult;
+ };
+ stx_transferSip10Nft: {
+ params: TransferNonFungibleParams;
+ result: TransactionResult;
+ };
+ stx_callContract: {
+ params: CallContractParams;
+ result: TransactionResult;
+ };
+ stx_deployContract: {
+ params: DeployContractParams;
+ result: TransactionResult;
+ };
+ stx_signTransaction: {
+ params: SignTransactionParams;
+ result: SignTransactionResult;
+ };
+ stx_signMessage: {
+ params: SignMessageParams;
+ result: SignMessageResult;
+ };
+ stx_signStructuredMessage: {
+ params: SignStructuredMessageParams;
+ result: SignMessageResult;
+ };
+ stx_getAddresses: {
+ params: GetAddressesParams;
+ result: GetAddressesResult;
+ };
+ stx_getAccounts: {
+ params: GetAccountsParams;
+ result: GetAccountsResult;
+ };
+ stx_updateProfile: {
+ params: UpdateProfileParams;
+ result: UpdateProfileResult;
+ };
+};
+
+export type MethodParams = Methods[M]['params'];
+
+export type MethodResult = Methods[M]['result'];
diff --git a/packages/connect/src/profile/index.ts b/packages/connect/src/profile/index.ts
index e013bcf1..bbc5cda1 100644
--- a/packages/connect/src/profile/index.ts
+++ b/packages/connect/src/profile/index.ts
@@ -1,81 +1,35 @@
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
-import {
- ProfileUpdatePayload,
- ProfileUpdatePopup,
- ProfileUpdateRequestOptions,
- StacksProvider,
-} from '../types';
+import { PublicPersonProfile } from '@stacks/profile';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
+import { ProfileUpdateRequestOptions, StacksProvider } from '../types';
+import { getStacksProvider } from '../utils';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultProfileUpdateRequestOptions(_options: ProfileUpdateRequestOptions) {}
-// eslint-disable-next-line @typescript-eslint/require-await
-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 = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: ProfileUpdateRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- ...defaults,
- };
-}
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const makeProfileUpdateToken = async (_options: ProfileUpdateRequestOptions) => {};
-async function openProfileUpdatePopup(
- { token, options }: ProfileUpdatePopup,
- provider: StacksProvider
-) {
- try {
- const profileUpdateResponse = await provider.profileUpdateRequest(token);
- options.onFinish?.(profileUpdateResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
+const METHOD = 'stx_updateProfile' as const;
-// eslint-disable-next-line @typescript-eslint/require-await
-export const makeProfileUpdateToken = async (options: ProfileUpdateRequestOptions) => {
- const { userSession, profile, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @internal */
+export const LEGACY_UPDATE_PROFILE_OPTIONS_MAP = (
+ options: ProfileUpdateRequestOptions
+): MethodParams => options;
- const payload: ProfileUpdatePayload = {
- ..._options,
- profile,
- publicKey,
- };
-
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- return createUnsecuredToken(payload as Json);
-};
-
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultProfileUpdateRequestOptions(options),
- ...options,
- } as T);
- return openProfileUpdatePopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_UPDATE_PROFILE_RESPONSE_MAP = (
+ response: MethodResult
+): PublicPersonProfile => response.profile as PublicPersonProfile;
+/** Compatible interface with previous Connect `openProfileUpdateRequestPopup` version, but using new SIP-030 RPC method. */
export function openProfileUpdateRequestPopup(
options: ProfileUpdateRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, makeProfileUpdateToken, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_UPDATE_PROFILE_OPTIONS_MAP,
+ LEGACY_UPDATE_PROFILE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/providers.ts b/packages/connect/src/providers.ts
index 459d1dfb..7f069c0a 100644
--- a/packages/connect/src/providers.ts
+++ b/packages/connect/src/providers.ts
@@ -15,7 +15,7 @@ export const DEFAULT_PROVIDERS: WebBTCProvider[] = [
mozillaAddOnsUrl: 'https://leather.io/install-extension',
},
{
- id: 'XverseProviders.StacksProvider',
+ id: 'XverseProviders.BitcoinProvider',
name: 'Xverse Wallet',
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9IiMxNzE3MTciIGQ9Ik0wIDBoNjAwdjYwMEgweiIvPjxwYXRoIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTQ0MCA0MzUuNHYtNTFjMC0yLS44LTMuOS0yLjItNS4zTDIyMCAxNjIuMmE3LjYgNy42IDAgMCAwLTUuNC0yLjJoLTUxLjFjLTIuNSAwLTQuNiAyLTQuNiA0LjZ2NDcuM2MwIDIgLjggNCAyLjIgNS40bDc4LjIgNzcuOGE0LjYgNC42IDAgMCAxIDAgNi41bC03OSA3OC43Yy0xIC45LTEuNCAyLTEuNCAzLjJ2NTJjMCAyLjQgMiA0LjUgNC42IDQuNUgyNDljMi42IDAgNC42LTIgNC42LTQuNlY0MDVjMC0xLjIuNS0yLjQgMS40LTMuM2w0Mi40LTQyLjJhNC42IDQuNiAwIDAgMSA2LjQgMGw3OC43IDc4LjRhNy42IDcuNiAwIDAgMCA1LjQgMi4yaDQ3LjVjMi41IDAgNC42LTIgNC42LTQuNloiLz48cGF0aCBmaWxsPSIjRUU3QTMwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0zMjUuNiAyMjcuMmg0Mi44YzIuNiAwIDQuNiAyLjEgNC42IDQuNnY0Mi42YzAgNCA1IDYuMSA4IDMuMmw1OC43LTU4LjVjLjgtLjggMS4zLTIgMS4zLTMuMnYtNTEuMmMwLTIuNi0yLTQuNi00LjYtNC42TDM4NCAxNjBjLTEuMiAwLTIuNC41LTMuMyAxLjNsLTU4LjQgNTguMWE0LjYgNC42IDAgMCAwIDMuMiA3LjhaIi8+PC9nPjwvc3ZnPg==',
@@ -30,7 +30,17 @@ export const DEFAULT_PROVIDERS: WebBTCProvider[] = [
id: 'AsignaProvider',
name: 'Asigna Multisig',
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMDEwMCIgZD0iTTAgMGgzMnYzMkgweiIvPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik0xNS4xMSA1LjU1YTMgMyAwIDAgMC0xLjgyIDEuM2wtLjA1LjA4LS40My43Mi0uMDcuMTEtLjUuODUtLjA1LjA5LTEuMjkgMi4xOC0uMDQuMDctLjQ3LjgtLjA2LjEtLjQ2Ljc4LS4wNy4xMS0xLjYzIDIuNzYtLjA3LjExLS4zOC42Ni0uMDUuMDgtLjczIDEuMjQtLjM1LjYtLjQuNjctLjA1LjA5TDUuMSAyMC43bC0uMTEuMTgtLjE0LjIzLS4wNy4xMy0uMzMuNTUtLjA0LjA3di4wMWExLjI2IDEuMjYgMCAwIDAtLjE0LjQ3IDEuMzEgMS4zMSAwIDAgMCAxLjI0IDEuNGgxLjVsLjA1LS4wNi4wNC0uMDYuODctMS4yMS4wNS0uMDguNzctMS4wNy4wNS0uMDcuNC0uNTcuMDUtLjA2LjI0LS4zNGExLjUyIDEuNTIgMCAwIDEgMS4zOS0uNjIgMS41IDEuNSAwIDAgMSAuNjQuMiAxLjQ3IDEuNDcgMCAwIDEgLjczIDEuMjcgMS40NCAxLjQ0IDAgMCAxLS4yNy44NGwtLjYzLjg4LS4wNS4wNy0uMzIuNDUtLjA2LjA4LS4wOC4xMi0uMTIuMTYtLjA1LjA4aDIuMTNhMi4zMiAyLjMyIDAgMCAwIDEuNzctLjk2bDEuMTgtMS42My43Ny0xLjA4IDEuMy0xLjhhMS4yNCAxLjI0IDAgMCAxIC41NS0uNDNsLjA4LS4wM2ExLjMgMS4zIDAgMCAxIC4zLS4wNiAxLjI4IDEuMjggMCAwIDEgMS4xNS41NGwuMTEuMmExLjEzIDEuMTMgMCAwIDEgLjEuNDEgMS4xOSAxLjE5IDAgMCAxLS4yMy43N2wtLjAzLjA1LS41Ny44LS43Ljk4LS4yNy4zN2ExLjIyIDEuMjIgMCAwIDAtLjIuNSAxLjA1IDEuMDUgMCAwIDAtLjAyLjIzdi4wNmExLjE3IDEuMTcgMCAwIDAgLjE0LjQzbC4wMi4wNS4wNy4xYTEuNDQgMS40NCAwIDAgMCAuMS4xMWwuMDUuMDYuMDEuMDFhMS44IDEuOCAwIDAgMCAuMTQuMWMwIC4wMi4wMi4wMy4wNC4wM2ExIDEgMCAwIDAgLjA4LjA1bC4wNy4wNGExLjI1IDEuMjUgMCAwIDAgLjUuMWg2LjljLjEgMCAuMi0uMDEuMjktLjAzbC4wNi0uMDJhMS4yNyAxLjI3IDAgMCAwIC4yNy0uMS41Ny41NyAwIDAgMCAuMDctLjAzIDEuMjEgMS4yMSAwIDAgMCAuMjYtLjE5bC4wOC0uMDdhLjkyLjkyIDAgMCAwIC4xNS0uMTkgMS41NSAxLjU1IDAgMCAwIC4wOS0uMTdsLjAyLS4wNWExLjIyIDEuMjIgMCAwIDAgLjA4LS4yNnYtLjA0bC4wMi0uMDh2LS4wOGExLjMyIDEuMzIgMCAwIDAtLjItLjc0bC0xLjYtMi42NC0uMDYtLjEtLjItLjMyLS4zMy0uNTR2LS4wMWwtLjA1LS4wOC0xLjMtMi4xNS0uMDctLjEtLjA0LS4wNi0uOC0xLjMyLS4wNC0uMDctLjItLjM0LS4xLS4xNC0uMS0uMTYtLjUzLS45LS4xMy0uMi0uMDktLjE0LTIuMTctMy41Ny0uMDQtLjA3LS43Mi0xLjE5LS4wNS0uMDctLjQtLjY1YTIuNjUgMi42NSAwIDAgMC0uMy0uNCAyLjk2IDIuOTYgMCAwIDAtLjk3LS43NCAzLjA0IDMuMDQgMCAwIDAtMS4zLS4zYy0uMjUgMC0uNS4wNC0uNzQuMVoiLz48cGF0aCBmaWxsPSJ1cmwoI2IpIiBkPSJNMTkgMTYuM2E1LjQ1IDUuNDUgMCAwIDAtLjgzIDEuNTZsLS4wNC4xNWExLjM2IDEuMzYgMCAwIDEgLjI4LS4xNiAxLjI0IDEuMjQgMCAwIDEgLjM4LS4wOGguMWExLjI4IDEuMjggMCAwIDEgMS4wNS41NGMuMDQuMDYuMDguMTMuMS4yYTEuMjQgMS4yNCAwIDAgMSAuMDkuMjcgMS4xOSAxLjE5IDAgMCAxLS4yLjkxbC0uMDQuMDUtLjU3Ljc5LS43Ljk5LS4yNy4zN2ExLjIzIDEuMjMgMCAwIDAtLjIuNDIgMS4wNiAxLjA2IDAgMCAwLS4wMi4zMXYuMDZhMS4xNyAxLjE3IDAgMCAwIC4xNi40Ny45My45MyAwIDAgMCAuMDcuMSAxLjUgMS41IDAgMCAwIC4xLjEybC4wNS4wNmguMDFhMS45NCAxLjk0IDAgMCAwIC4wOS4wOCAxIDEgMCAwIDAgLjE3LjFsLjA3LjA0YTEuMjUgMS4yNSAwIDAgMCAuNS4xaDYuOWMuMSAwIC4yIDAgLjI4LS4wMmwuMDctLjAyYTEuMzIgMS4zMiAwIDAgMCAuMzQtLjEzbC4xNi0uMS4wMy0uMDNhMS4yOSAxLjI5IDAgMCAwIC4yLS4yIDIuNDMgMi40MyAwIDAgMCAuMTItLjE3Yy4wMy0uMDMuMDUtLjA4LjA3LS4xMmwuMDItLjA1YTEuMjEgMS4yMSAwIDAgMCAuMDktLjN2LS4wOGwuMDEtLjA5YTEuMzIgMS4zMiAwIDAgMC0uMi0uNzNsLTEuNi0yLjY0LS4wNi0uMS0uMi0uMzItLjMzLS41NHYtLjAybC0uMDUtLjA3LTEuMy0yLjE1LS4xMi0uMDctLjA3LS4wNGE0Ljk0IDQuOTQgMCAwIDAtMi40Ni0uNjdjLTEuMDMgMC0xLjc2LjU3LTIuMjYgMS4yWiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMi4yOSAyMS4wOGMwIC4yOS0uMDkuNTgtLjI3Ljg0bC0xLjMxIDEuODRIN2wyLjUyLTMuNTNhMS41NCAxLjU0IDAgMCAxIDIuMS0uMzZjLjQzLjI4LjY2Ljc0LjY2IDEuMloiLz48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTEuMTYgMjEuMjVhLjU2LjU2IDAgMCAxLS41Ny41NS41Ni41NiAwIDAgMS0uNTctLjU2LjU2LjU2IDAgMCAxIC41Ny0uNTUuNTYuNTYgMCAwIDEgLjU3LjU2WiIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjE1LjIzIiB4Mj0iMTkuMyIgeTE9IjI1Ljc4IiB5Mj0iNi4xMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM2NTIyRjQiLz48c3RvcCBvZmZzZXQ9Ii41NSIgc3RvcC1jb2xvcj0iIzlCNkJGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0E1ODVGRiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMjIuNTkiIHgyPSIyNC44IiB5MT0iMjQuNzEiIHkyPSIxNS41MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM0MjFGOEIiLz48c3RvcCBvZmZzZXQ9Ii41NSIgc3RvcC1jb2xvcj0iIzcyMzBGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzk3NzNGRiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPg==',
+
webUrl: 'https://asigna.io',
chromeWebStoreUrl: 'https://stx.asigna.io/',
},
+ {
+ id: 'FordefiProviders.UtxoProvider',
+ name: 'Fordefi',
+
+ icon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZmlsbD0iIzEwMTExNCIgZD0iTTAgMGg0MnY0MkgweiIvPgogIDxwYXRoIGQ9Ik0xOS40NyAyNi44OUg1djMuNTdhNC41NyA0LjU3IDAgMCAwIDQuNTggNC41N2g1LjgzbDQuMDYtOC4xNFoiIGZpbGw9IiM3OTk0RkYiLz4KICA8cGF0aCBkPSJNNSAxNy40aDI3LjU4bC0zLjIgNi43OEg1VjE3LjRaIiBmaWxsPSIjNDg2REZGIi8+CiAgPHBhdGggZD0iTTE0LjY3IDdINXY3LjY4aDMzVjdoLTkuNjd2NS43NGgtMlY3aC05LjY3djUuNzRoLTEuOTlWN1oiIGZpbGw9IiM1Q0QxRkEiLz4KPC9zdmc+Cg==',
+ webUrl: 'https://www.fordefi.com/',
+ chromeWebStoreUrl:
+ 'https://chromewebstore.google.com/detail/fordefi/hcmehenccjdmfbojapcbcofkgdpbnlle',
+ },
];
diff --git a/packages/connect/src/request.ts b/packages/connect/src/request.ts
new file mode 100644
index 00000000..f577a59c
--- /dev/null
+++ b/packages/connect/src/request.ts
@@ -0,0 +1,302 @@
+import { getInstalledProviders, getProvider, WbipProvider } from '@stacks/connect-ui';
+import { defineCustomElements } from '@stacks/connect-ui/loader';
+import { JsonRpcError, JsonRpcErrorCode } from './errors';
+import { MethodParams, MethodResult, Methods } from './methods';
+import { DEFAULT_PROVIDERS } from './providers';
+import { StacksProvider } from './types';
+import { base64 } from '@scure/base';
+import { bytesToHex } from '@stacks/common';
+import { Cl } from '@stacks/transactions';
+
+export interface ConnectRequestOptions {
+ /**
+ * The provider to use for the request.
+ * If none is provided the UI will be shown.
+ * Defaults to the previously selected provider (unless `forceWalletSelect` is `true`).
+ */
+ provider?: StacksProvider;
+
+ /**
+ * The default wallets to display in the modal.
+ * Defaults to some known popular wallets.
+ */
+ defaultProviders?: WbipProvider[];
+
+ /**
+ * Forces the user to select a wallet.
+ * Defaults to `false`.
+ */
+ forceWalletSelect?: boolean;
+
+ /**
+ * Persist the selected wallet across requests.
+ * Defaults to `true`.
+ */
+ persistWalletSelect?: boolean;
+
+ /**
+ * Adds manual request rewriting to make different providers behave more closely to SIP-030 / WBIPs.
+ * Defaults to `true`.
+ */
+ enableOverrides?: boolean;
+
+ // todo: maybe add callbacks, if set use them instead of throwing errors
+}
+
+export async function requestRaw(
+ provider: StacksProvider,
+ method: M,
+ params?: MethodParams
+): Promise> {
+ try {
+ const response = await provider.request(method, params);
+ if ('error' in response) throw JsonRpcError.fromResponse(response.error);
+
+ return response.result;
+ } catch (error) {
+ if (error instanceof JsonRpcError) throw error;
+ if ('jsonrpc' in error) throw JsonRpcError.fromResponse(error.error);
+
+ const code = error.code ?? JsonRpcErrorCode.UnknownError;
+ throw new JsonRpcError(error.message, code, error.data, error);
+ }
+}
+
+export async function request(
+ method: M,
+ params?: MethodParams
+): Promise>;
+export async function request(
+ options: ConnectRequestOptions,
+ method: M,
+ params?: MethodParams
+): Promise>;
+export async function request(
+ ...args:
+ | [method: M, params?: MethodParams]
+ | [options: ConnectRequestOptions, method: M, params?: MethodParams]
+): Promise> {
+ const { options, method, params } = requestArgs(args);
+
+ // Default options
+ const opts = Object.assign(
+ {
+ provider: getProvider(),
+ defaultProviders: DEFAULT_PROVIDERS,
+
+ forceWalletSelect: false,
+ persistWalletSelect: true,
+ enableOverrides: true,
+ },
+ shallowDefined(options)
+ );
+
+ // WITHOUT UI
+ if (opts.provider && !opts.forceWalletSelect) {
+ const { method: finalMethod, params: finalParams } = getMethodOverrides(
+ opts.provider,
+ method,
+ params,
+ opts.enableOverrides
+ );
+ return await requestRaw(opts.provider, finalMethod as any, serializeParams(finalParams));
+ }
+
+ // WITH UI
+ if (typeof window === 'undefined') return undefined; // don't throw for SSR contexts
+
+ void defineCustomElements(window);
+
+ return new Promise((resolve, reject) => {
+ const element = document.createElement('connect-modal');
+ element.defaultProviders = opts.defaultProviders;
+ element.installedProviders = getInstalledProviders(opts.defaultProviders);
+ element.persistWalletSelect = opts.persistWalletSelect;
+
+ const originalOverflow = document.body.style.overflow;
+ document.body.style.overflow = 'hidden';
+
+ const closeModal = () => {
+ element.remove();
+ document.body.style.overflow = originalOverflow;
+ };
+
+ element.callback = (selectedProvider: StacksProvider | undefined) => {
+ closeModal();
+
+ const { method: finalMethod, params: finalParams } = getMethodOverrides(
+ selectedProvider,
+ method,
+ params,
+ opts.enableOverrides
+ );
+
+ resolve(requestRaw(selectedProvider, finalMethod as any, serializeParams(finalParams)));
+ };
+
+ element.cancelCallback = () => {
+ closeModal();
+ reject(new JsonRpcError('User canceled the request', JsonRpcErrorCode.UserCanceled));
+ };
+
+ document.body.appendChild(element);
+
+ const handleEsc = (ev: KeyboardEvent) => {
+ if (ev.key !== 'Escape') return;
+ document.removeEventListener('keydown', handleEsc);
+ element.remove();
+ reject(new JsonRpcError('User canceled the request', JsonRpcErrorCode.UserCanceled));
+ };
+ document.addEventListener('keydown', handleEsc);
+ });
+}
+
+/** @internal */
+function requestArgs(
+ args:
+ | [method: M, params?: MethodParams]
+ | [options: ConnectRequestOptions, method: M, params?: MethodParams]
+): {
+ method: M;
+ params?: MethodParams;
+ options?: ConnectRequestOptions;
+} {
+ if (typeof args[0] === 'string') return { method: args[0], params: args[1] as MethodParams };
+ return { options: args[0], method: args[1] as M, params: args[2] };
+}
+
+/**
+ * **Note:** Higher order function!
+ * @internal Legacy non-UI request.
+ */
+export function requestRawLegacy(
+ method: M,
+ mapOptions: (options: O) => MethodParams,
+ mapResponse: (response: MethodResult) => R
+) {
+ return (options: O, provider?: StacksProvider) => {
+ if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
+
+ const params = mapOptions(options);
+
+ // Manual cast, since TypeScipt can't infer generic type of options
+ const o = options as {
+ onCancel?: (error?: Error) => void;
+ onFinish?: (response: R) => void;
+ };
+
+ const { method: finalMethod, params: finalParams } = getMethodOverrides(
+ provider,
+ method,
+ params
+ );
+
+ void requestRaw(provider, finalMethod as any, serializeParams(finalParams))
+ .then(response => {
+ const r = mapResponse(response);
+ o.onFinish?.(r);
+ })
+ .catch(o.onCancel);
+ };
+}
+
+// todo: strip params that might be unserializable or privacy sensitive, e.g.
+// appDetails?: AuthOptions['appDetails'];
+// authOrigin?: string;
+// network?: ConnectNetwork;
+// stxAddress?: string;
+// userSession?: UserSession;
+// onFinish?: ProfileUpdateFinished;
+// onCancel?: ProfileUpdateCanceled;
+
+function isXverse(provider: StacksProvider): boolean {
+ return (
+ // Best effort detection for Xverse
+ 'signMultipleTransactions' in provider &&
+ 'createRepeatInscriptions' in provider &&
+ !provider?.['isLeather'] &&
+ !provider?.['isFordefi']
+ );
+}
+
+function isFordefi(provider: StacksProvider): boolean {
+ return 'isFordefi' in provider && !!provider.isFordefi;
+}
+
+function isLeather(provider: StacksProvider): boolean {
+ return 'isLeather' in provider && !!provider.isLeather;
+}
+
+function shallowDefined(obj: T): Partial {
+ if (obj === undefined) return {};
+ const result: Partial = {};
+ for (const [key, value] of Object.entries(obj)) {
+ if (value !== undefined) result[key as keyof T] = value;
+ }
+ return result;
+}
+
+function getMethodOverrides(
+ provider: StacksProvider,
+ method: M,
+ params?: MethodParams,
+ enableOverrides: boolean = true
+): { method: string; params: any } {
+ if (!enableOverrides) return { method, params };
+
+ // Xverse-ish wallets
+ if (
+ (isXverse(provider) || isFordefi(provider)) &&
+ // Permission granting method
+ ['getAddresses', 'stx_getAddresses'].includes(method)
+ ) {
+ return { method: 'wallet_connect', params };
+ }
+
+ // Leather `signPsbt`
+ if (isLeather(provider) && method === 'signPsbt') {
+ const paramsLeather = {
+ hex: bytesToHex(base64.decode((params as MethodParams<'signPsbt'>).psbt)),
+ signAtIndex: (params as MethodParams<'signPsbt'>).signInputs.map(i => {
+ if (typeof i === 'number') return i;
+ return i.index;
+ }),
+ allowedSighash: (params as MethodParams<'signPsbt'>).allowedSighash,
+ };
+ return { method, params: paramsLeather };
+ }
+
+ return { method, params };
+}
+
+/**
+ * @internal
+ * Simple function for serializing clarity object values to hex strings, in case wallets don't support them.
+ */
+function serializeParams(params: MethodParams): MethodParams {
+ if (!params || typeof params !== 'object') return params;
+
+ const result = { ...params };
+
+ for (const [key, value] of Object.entries(params)) {
+ if (!value) continue;
+
+ // Handle array of Clarity values
+ if (Array.isArray(value)) {
+ result[key] = value.map(item => {
+ if (item && typeof item === 'object' && 'type' in item) {
+ return Cl.serialize(item);
+ }
+ return item;
+ });
+ continue;
+ }
+
+ // Handle direct Clarity value
+ if (typeof value === 'object' && 'type' in value) {
+ result[key] = Cl.serialize(value);
+ }
+ }
+
+ return result;
+}
diff --git a/packages/connect/src/signature/index.ts b/packages/connect/src/signature/index.ts
index 09c3e6db..5e67058c 100644
--- a/packages/connect/src/signature/index.ts
+++ b/packages/connect/src/signature/index.ts
@@ -1,99 +1,43 @@
-import { ChainId } from '@stacks/network';
-import { createUnsecuredToken, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
import { StacksProvider } from '../types';
import {
CommonSignatureRequestOptions,
- SignatureOptions,
- SignaturePayload,
- SignaturePopup,
+ SignatureData,
SignatureRequestOptions,
} from '../types/signature';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+import { getStacksProvider } from '../utils';
-function getStxAddress(options: CommonSignatureRequestOptions) {
- const { userSession, network: _network } = options;
-
- if (!userSession || !_network) return undefined;
- const stxAddresses = userSession?.loadUserData().profile?.stxAddress;
- const chainIdToKey = {
- [ChainId.Mainnet]: 'mainnet',
- [ChainId.Testnet]: 'testnet',
- };
- const network = legacyNetworkFromConnectNetwork(_network);
- const address: string | undefined = stxAddresses?.[chainIdToKey[network.chainId]];
- return address;
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: SignaturePayload, 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 getDefaultSignatureRequestOptions(options: CommonSignatureRequestOptions) {
- const network = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: CommonSignatureRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- stxAddress: getStxAddress(defaults),
- ...defaults,
- };
-}
-
-async function openSignaturePopup({ token, options }: SignaturePopup, provider: StacksProvider) {
- try {
- const signatureResponse = await provider.signatureRequest(token);
- options.onFinish?.(signatureResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultSignatureRequestOptions(_options: CommonSignatureRequestOptions) {}
export interface SignatureRequestPayload {
message: string;
}
-// eslint-disable-next-line @typescript-eslint/require-await
-export const signMessage = async (options: SignatureRequestOptions) => {
- const { userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const signMessage = async (_options: SignatureRequestOptions) => {};
- const payload: SignaturePayload = {
- ..._options,
- publicKey,
- };
+const METHOD = 'stx_signMessage' as const;
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- return createUnsecuredToken(payload as any);
-};
+/** @internal */
+export const LEGACY_SIGN_MESSAGE_OPTIONS_MAP = (
+ options: SignatureRequestOptions
+): MethodParams => options;
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultSignatureRequestOptions(options),
- ...options,
- } as T);
- return openSignaturePopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_SIGN_MESSAGE_RESPONSE_MAP = (
+ response: MethodResult
+): SignatureData => response;
+/** Compatible interface with previous Connect `openSignatureRequestPopup` version, but using new SIP-030 RPC method. */
export function openSignatureRequestPopup(
options: SignatureRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, signMessage, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_MESSAGE_OPTIONS_MAP,
+ LEGACY_SIGN_MESSAGE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/signature/structuredData.ts b/packages/connect/src/signature/structuredData.ts
index f661cc1d..2553e24a 100644
--- a/packages/connect/src/signature/structuredData.ts
+++ b/packages/connect/src/signature/structuredData.ts
@@ -1,94 +1,42 @@
-import { bytesToHex } from '@stacks/common';
-import {
- serializeCV as legacySerializeCV,
- ClarityValue as LegacyClarityValue,
- TupleCV as LegacyTupleCV,
-} from '@stacks/transactions-v6';
-import { serializeCV } from '@stacks/transactions';
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getDefaultSignatureRequestOptions } from '.';
-import { getKeys, hasAppPrivateKey } from '../transactions';
-import {
- StructuredDataSignatureOptions,
- StructuredDataSignaturePayload,
- StructuredDataSignaturePopup,
- StructuredDataSignatureRequestOptions,
-} from '../types/structuredDataSignature';
-import { getStacksProvider } from '../utils';
-import { StacksProvider } from '../types';
-
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultSignatureRequestOptions(options),
- ...options,
- } as T);
- return openStructuredDataSignaturePopup({ token, options }, provider);
-}
-
-function parseUnserializableBigIntValues(payload: StructuredDataSignaturePayload) {
- const { message, domain } = payload;
-
- if (typeof message.type === 'string' && typeof domain.type === 'string') {
- // new readable types
- return {
- ...payload,
- message: serializeCV(message),
- domain: serializeCV(domain),
- } as Json;
- }
-
- // legacy types
- return {
- ...payload,
- message: bytesToHex(legacySerializeCV(message as LegacyClarityValue)),
- domain: bytesToHex(legacySerializeCV(domain as LegacyTupleCV)),
- } as Json;
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: StructuredDataSignaturePayload, privateKey: string) {
- const tokenSigner = new TokenSigner('ES256k', privateKey);
- return tokenSigner.signAsync(parseUnserializableBigIntValues(payload));
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-export async function signStructuredMessage(options: StructuredDataSignatureRequestOptions) {
- const { userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
- const payload: StructuredDataSignaturePayload = {
- ..._options,
- publicKey,
- };
- return signPayload(payload, privateKey);
- }
- return createUnsecuredToken(
- parseUnserializableBigIntValues(options as StructuredDataSignaturePayload)
- );
-}
-
-async function openStructuredDataSignaturePopup(
- { token, options }: StructuredDataSignaturePopup,
- provider: StacksProvider
-) {
- try {
- const signatureResponse = await provider.structuredDataSignatureRequest(token);
-
- options.onFinish?.(signatureResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
-
+import { TupleCV } from '@stacks/transactions';
+import { ClarityType as LegacyClarityType } from '@stacks/transactions-v6';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
+import { SignatureData, StacksProvider } from '../types';
+import { StructuredDataSignatureRequestOptions } from '../types/structuredDataSignature';
+import { getStacksProvider, legacyCVToCV } from '../utils';
+
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export async function signStructuredMessage(_options: StructuredDataSignatureRequestOptions) {}
+
+const METHOD = 'stx_signStructuredMessage' as const;
+
+/** @internal */
+export const LEGACY_SIGN_STRUCTURED_MESSAGE_OPTIONS_MAP = (
+ options: StructuredDataSignatureRequestOptions
+): MethodParams => ({
+ // todo: also make sure that cvs don't have bigint unserializable values
+ message: legacyCVToCV(options.message),
+ domain: legacyCVToCV(options.domain) as TupleCV, // safe cast, because of below check
+});
+
+/** @internal */
+export const LEGACY_SIGN_STRUCTURED_MESSAGE_RESPONSE_MAP = (
+ response: MethodResult
+): SignatureData => response;
+
+/** Compatible interface with previous Connect `openStructuredDataSignatureRequestPopup` version, but using new SIP-030 RPC method. */
export function openStructuredDataSignatureRequestPopup(
options: StructuredDataSignatureRequestOptions,
provider: StacksProvider = getStacksProvider()
-) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, signStructuredMessage, provider);
+): void {
+ if (options.domain.type !== LegacyClarityType.Tuple) {
+ throw new Error('Domain must be a tuple'); // check, ensures domain is a tuple
+ }
+
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_STRUCTURED_MESSAGE_OPTIONS_MAP,
+ LEGACY_SIGN_STRUCTURED_MESSAGE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/stories/ConnectPage.tsx b/packages/connect/src/stories/ConnectPage.tsx
index 61f1989a..554103c6 100644
--- a/packages/connect/src/stories/ConnectPage.tsx
+++ b/packages/connect/src/stories/ConnectPage.tsx
@@ -1,8 +1,10 @@
-import { AppConfig, UserSession } from '@stacks/auth';
+import React from 'react';
import { getSelectedProviderId } from '@stacks/connect-ui';
import { Cl } from '@stacks/transactions';
import { useReducer, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
+import { UserSession } from '../auth';
+import { request } from '../request';
import {
disconnect,
showConnect,
@@ -25,41 +27,7 @@ BigInt.prototype.toJSON = function () {
return this.toString();
};
-const userSession = new UserSession({
- appConfig: new AppConfig(['store_write', 'publish_data']),
-});
-
-// Form Types
-type SignMessageFormData = {
- message: string;
-};
-
-type STXTransferFormData = {
- amount: string;
- recipient: string;
-};
-
-type ContractCallFormData = {
- contractAddress: string;
- contractName: string;
- functionName: string;
- functionArgs: string;
-};
-
-type ContractDeployFormData = {
- contractName: string;
- codeBody: string;
- clarityVersion?: string;
-};
-
-type LegacySignTransactionFormData = {
- txHex: string;
-};
-
-type LegacySignStructuredMessageFormData = {
- message: string;
- domain: string;
-};
+const userSession = new UserSession();
const appDetails = {
name: 'Connect',
@@ -68,12 +36,16 @@ const appDetails = {
// Legacy Sign Message Form Component
const LegacySignMessageForm = () => {
+ type SignMessageFormData = {
+ message: string;
+ };
const methods = useForm();
const { register, handleSubmit } = methods;
const refresh = useReducer(x => x + 1, 0)[1];
const [response, setResponse] = useState(null);
- const onSubmit = handleSubmit(({ message }) => {
+ const onSubmit = handleSubmit(({ message }, e) => {
+ e.preventDefault();
showSignMessage({
message,
appDetails,
@@ -81,17 +53,17 @@ const LegacySignMessageForm = () => {
setResponse(d);
refresh();
},
- onCancel: () => {
- setResponse({ error: 'User canceled the request' });
+ onCancel: e => {
+ setResponse({ onCancel: e?.toString() });
},
- });
+ } as any);
});
return (